Merge branch 'master' into ab-fetch-token-counters-in-parallel

pull/2666/head
Victor Baranov 5 years ago committed by GitHub
commit f12e61e865
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      CHANGELOG.md
  2. 4
      apps/block_scout_web/assets/close.svg
  3. 1
      apps/block_scout_web/assets/css/_images-preload.scss
  4. 7
      apps/block_scout_web/assets/css/components/_card.scss
  5. 2
      apps/block_scout_web/assets/css/components/_dashboard-banner.scss
  6. 2
      apps/block_scout_web/assets/css/components/_dropdown.scss
  7. 140
      apps/block_scout_web/assets/css/components/_navbar.scss
  8. 3
      apps/block_scout_web/assets/css/components/_network-selector.scss
  9. 12
      apps/block_scout_web/assets/css/components/_tile.scss
  10. 58
      apps/block_scout_web/assets/css/theme/_dai_variables.scss
  11. 47
      apps/block_scout_web/assets/css/theme/_dark-theme.scss
  12. 1
      apps/block_scout_web/assets/css/theme/_variables-non-critical.scss
  13. 1
      apps/block_scout_web/assets/css/theme/_variables.scss
  14. 7
      apps/block_scout_web/assets/css/theme/_xusdt_variables-non-critical.scss
  15. 97
      apps/block_scout_web/assets/css/theme/_xusdt_variables.scss
  16. 9
      apps/block_scout_web/assets/js/pages/dark-mode-switcher.js
  17. 37
      apps/block_scout_web/assets/js/pages/layout.js
  18. 14
      apps/block_scout_web/assets/static/images/dai_logo.svg
  19. 4
      apps/block_scout_web/assets/static/images/network-selector-icons/circle-xusdt.svg
  20. 4
      apps/block_scout_web/assets/static/images/network-selector-icons/xdai-chain.svg
  21. 3
      apps/block_scout_web/assets/static/images/xusdt-logo-footer.svg
  22. 4
      apps/block_scout_web/assets/static/images/xusdt-logo-top.svg
  23. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex
  24. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex
  25. 28
      apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex
  26. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex
  27. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex
  28. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex
  29. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex
  30. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex
  31. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex
  32. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  33. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex
  34. 12
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  35. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  36. 49
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  37. 48
      apps/block_scout_web/lib/block_scout_web/templates/common_components/_tile-loader.html.eex
  38. 32
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  39. 2
      apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
  40. 9
      apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex
  41. 43
      apps/block_scout_web/priv/gettext/default.pot
  42. 43
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  43. 20
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
  44. 47
      apps/block_scout_web/test/block_scout_web/views/address_contract_view_test.exs
  45. 4
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex
  46. 23
      apps/explorer/config/config.exs
  47. 4
      apps/explorer/lib/explorer/application.ex
  48. 22
      apps/explorer/lib/explorer/chain.ex
  49. 4
      apps/explorer/lib/explorer/chain/cache/block_count.ex
  50. 4
      apps/explorer/lib/explorer/chain/cache/block_number.ex
  51. 4
      apps/explorer/lib/explorer/chain/cache/blocks.ex
  52. 4
      apps/explorer/lib/explorer/chain/cache/transaction_count.ex
  53. 4
      apps/explorer/lib/explorer/chain/cache/transactions.ex
  54. 163
      apps/explorer/lib/explorer/chain/import/runner/blocks.ex
  55. 42
      apps/explorer/lib/explorer/chain/ordered_cache.ex
  56. 39
      apps/explorer/lib/explorer/chain/smart_contract.ex
  57. 54
      apps/explorer/lib/explorer/chain_spec/parity/importer.ex
  58. 89
      apps/explorer/lib/explorer/chain_spec/poa/importer.ex
  59. 32
      apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex
  60. 7
      apps/explorer/priv/repo/migrations/20190910170703_create_indexes_for_block_number_in_token_transfers_and_transactions.exs
  61. 6
      apps/explorer/test/explorer/chain/cache/block_number_test.exs
  62. 114
      apps/explorer/test/explorer/chain/import/runner/blocks_test.exs
  63. 101
      apps/explorer/test/explorer/exchange_rates/source/coin_gecko_test.exs
  64. 14
      apps/indexer/test/indexer/block/fetcher/receipts_test.exs
  65. 9
      docs/env-variables.md

@ -1,9 +1,32 @@
## Current
### Features
- [#2679](https://github.com/poanetwork/blockscout/pull/2679) - added fixed height for card chain blocks and card chain transactions
- [#2678](https://github.com/poanetwork/blockscout/pull/2678) - fixed dashboard banner height bug
- [#2672](https://github.com/poanetwork/blockscout/pull/2672) - added new theme for xUSDT
- [#2666](https://github.com/poanetwork/blockscout/pull/2666) - fetch token counters in parallel
- [#2665](https://github.com/poanetwork/blockscout/pull/2665) - new menu layout for mobile devices
- [#2663](https://github.com/poanetwork/blockscout/pull/2663) - Fetch address counters in parallel
### Fixes
- [#2701](https://github.com/poanetwork/blockscout/pull/2701) - Exclude nonconsensus blocks from avg block time calculation by default
- [#2693](https://github.com/poanetwork/blockscout/pull/2693) - remove non consensus internal transactions
- [#2687](https://github.com/poanetwork/blockscout/pull/2687) - remove non-consensus token transfers, logs when inserting new consensus blocks
- [#2684](https://github.com/poanetwork/blockscout/pull/2684) - do not filter pending logs
- [#2682](https://github.com/poanetwork/blockscout/pull/2682) - Use Task.start instead of Task.async in caches
- [#2671](https://github.com/poanetwork/blockscout/pull/2671) - fixed buttons color at smart contract section
- [#2691](https://github.com/poanetwork/blockscout/pull/2691) - fix exchange rate websocket update for Rootstock
### Chore
## 2.0.4-beta
### Features
- [#2636](https://github.com/poanetwork/blockscout/pull/2636) - Execute all address' transactions page queries in parallel
- [#2596](https://github.com/poanetwork/blockscout/pull/2596) - support AuRa's empty step reward type
- [#2588](https://github.com/poanetwork/blockscout/pull/2588) - add verification submission comment
- [#2505](https://github.com/poanetwork/blockscout/pull/2505) - support POA Network emission rewards
- [#2581](https://github.com/poanetwork/blockscout/pull/2581) - Add generic Map-like Cache behaviour and implementation
- [#2561](https://github.com/poanetwork/blockscout/pull/2561) - Add token's type to the response of tokenlist method
- [#2555](https://github.com/poanetwork/blockscout/pull/2555) - find and show decoding candidates for logs
@ -12,14 +35,14 @@
### Fixes
- [#2659](https://github.com/poanetwork/blockscout/pull/2659) - Multipurpose front-end part update
- [#2468](https://github.com/poanetwork/blockscout/pull/2468) - fix confirmations for non consensus blocks
- [#2610](https://github.com/poanetwork/blockscout/pull/2610) - use CoinGecko instead of CoinMarketcap for exchange rates
- [#2640](https://github.com/poanetwork/blockscout/pull/2640) - SVG network icons
- [#2635](https://github.com/poanetwork/blockscout/pull/2635) - optimize ERC721 inventory query
- [#2626](https://github.com/poanetwork/blockscout/pull/2626) - Fixing 2 Mobile UI Issues
- [#2623](https://github.com/poanetwork/blockscout/pull/2623) - fix a blinking test
- [#2616](https://github.com/poanetwork/blockscout/pull/2616) - deduplicate coin history records by delta
- [#2613](https://github.com/poanetwork/blockscout/pull/2613) - fix getminedblocks rpc endpoint
- [#2612](https://github.com/poanetwork/blockscout/pull/2612) - Add cache updating independently from Indexer
- [#2610](https://github.com/poanetwork/blockscout/pull/2610) - use CoinGecko instead of CoinMarketcap for exchange rates
- [#2592](https://github.com/poanetwork/blockscout/pull/2592) - process new metadata format for whisper
- [#2591](https://github.com/poanetwork/blockscout/pull/2591) - Fix url error in API page
- [#2572](https://github.com/poanetwork/blockscout/pull/2572) - Ease non-critical css
@ -29,8 +52,10 @@
- [#2564](https://github.com/poanetwork/blockscout/pull/2564) - fix first page button for uncles and reorgs
- [#2563](https://github.com/poanetwork/blockscout/pull/2563) - Fix view less transfers button
- [#2538](https://github.com/poanetwork/blockscout/pull/2538) - fetch the last not empty coin balance records
- [#2468](https://github.com/poanetwork/blockscout/pull/2468) - fix confirmations for non consensus blocks
### Chore
- [#2662](https://github.com/poanetwork/blockscout/pull/2662) - fetch coin gecko id based on the coin symbol
- [#2646](https://github.com/poanetwork/blockscout/pull/2646) - Added Xerom to list of Additional Chains using BlockScout
- [#2634](https://github.com/poanetwork/blockscout/pull/2634) - add Lukso to networks dropdown
- [#2617](https://github.com/poanetwork/blockscout/pull/2617) - skip cache update if there are no blocks inserted

@ -0,0 +1,4 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 47.971 47.971" style="enable-background:new 0 0 47.971 47.971;" xml:space="preserve"><g><g>
<path d="M28.228,23.986L47.092,5.122c1.172-1.171,1.172-3.071,0-4.242c-1.172-1.172-3.07-1.172-4.242,0L23.986,19.744L5.121,0.88 c-1.172-1.172-3.07-1.172-4.242,0c-1.172,1.171-1.172,3.071,0,4.242l18.865,18.864L0.879,42.85c-1.172,1.171-1.172,3.071,0,4.242 C1.465,47.677,2.233,47.97,3,47.97s1.535-0.293,2.121-0.879l18.865-18.864L42.85,47.091c0.586,0.586,1.354,0.879,2.121,0.879 s1.535-0.293,2.121-0.879c1.172-1.171,1.172-3.071,0-4.242L28.228,23.986z" data-original="#000000" class="active-path" data-old_color="#000000" fill="#828BA0"/>
</g></g> </svg>

After

Width:  |  Height:  |  Size: 806 B

@ -13,4 +13,5 @@ body:after {
url(/images/network-selector-icons/ropsten-testnet.svg)
url(/images/network-selector-icons/xdai-chain.svg)
url(/images/network-selector-icons/lukso-l14-testnet.svg)
url(/images/network-selector-icons/circle-xusdt.svg)
};

@ -155,8 +155,8 @@ $card-tab-icon-color-active: #fff !default;
}
.card-chain-blocks {
height: auto;
min-height: 233px;
max-height: auto;
[class*="col-"]:last-child {
.tile {
margin-bottom: 0;
@ -165,7 +165,8 @@ $card-tab-icon-color-active: #fff !default;
}
.card-chain-transactions {
height: auto;
min-height: 664px;
max-height: auto;
.tile {
margin-bottom: 0;

@ -148,7 +148,7 @@ $dashboard-banner-chart-axis-font-color: $dashboard-stats-item-value-color !defa
display: flex;
height: $dashboard-banner-network-plain-container-height;
justify-content: center;
margin: 0 0 0 30px;
margin: 45px 0 0 30px;
max-width: 100%;
padding: 30px 0 30px 60px;
width: 750px;

@ -1,6 +1,6 @@
$dropdown-menu-item-color: #333 !default;
$dropdown-menu-item-hover-color: $secondary !default;
$dropdown-menu-item-hover-background: rgba($secondary, .1) !default;
$dropdown-menu-item-hover-background: rgba($secondary, 0.1) !default;
// These styles extend the default Bootstrap styles
.dropdown-menu {

@ -92,7 +92,7 @@ $navbar-logo-width: auto !default;
}
.navbar-toggler {
border-color: $primary;
outline: none;
color: $primary;
.navbar-toggler-icon {
@ -224,7 +224,7 @@ $navbar-logo-width: auto !default;
}
.navbar-logo {
max-height: $navbar-logo-height;
height: $navbar-logo-height;
width: $navbar-logo-width;
}
@ -258,4 +258,140 @@ $navbar-logo-width: auto !default;
.topnav-nav-link {
transition: none !important;
}
}
.nav-link-new {
margin-left: auto;
font-size: 14px;
color: #333333;
@include media-breakpoint-up(lg) {
display: none;
}
}
.new-button {
margin-right: -7px !important;
padding-right: 0px;
height: 20px!important;
border-left: 1px solid #a3a9b5!important;
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
background: transparent;
border: none;
cursor: pointer;
outline: none !important;
box-shadow: none !important;
transition: .2s ease-in;
@include media-breakpoint-up(lg) {
display: none!important;
}
}
@media (max-width: 992px) {
.navbar.navbar-primary .navbar-nav .nav-link .nav-link-icon {
display: none;
}
.navbar-nav .nav-item .dropdown-toggle::after {
display: none;
}
.navbar.navbar-primary .navbar-nav .nav-link {
font-size: 28px;
color: #333333;
padding: 0.7rem 1.4rem;
font-weight: 600;
}
.navbar .dropdown-menu {
box-shadow: none;
}
.navbar .dropdown-menu .dark-mode-changer {
display: none;
}
.nav-item .dropdown {
padding: 0px 15%;
font-weight: 600;
}
.last-child {
display: none!important;
}
.search-form {
width: 100%!important;
}
.awesomplete {
height: 50px;
}
.navbar.navbar-primary .input-group-append {
left: 15px !important;
}
.navbar.navbar-primary .form-control {
padding-right: 30px;
padding-left: 13.5%;
}
.dropdown-item {
padding: 10px 1.6rem !important;
color: rgba(51, 51, 51, 0.7)!important;
}
.navbar.navbar-primary .navbar-nav .nav-link:hover,
.navbar.navbar-primary .navbar-nav .nav-link.activeLink,
.navbar.navbar-primary .navbar-nav .nav-link:focus {
background-image: linear-gradient(to right, #f5f6fa, rgba(245, 246, 250, 0));
width: 100%;
}
.navbar.navbar-primary .navbar-nav .nav-link:hover:before {
content: "|";
height: 50px;
width: 50%;
opacity: 1;
background: none;
left: 24%;
top: 14%;
color: $primary;
}
.navbar {
padding: 0.5rem 0rem!important;
}
.navbar-brand {
margin-right: 0px!important;
margin-left: 1rem;
}
.dropdown-item.active,
.dropdown-item:hover,
.dropdown-item:focus {
background-image: linear-gradient(to right, #f5f6fa, rgba(245, 246, 250, 0));
width: 100%;
background-color: white!important;
}
.dropdown-item:hover:before {
content: "|";
height: 50px;
width: 50%;
opacity: 1;
background: none;
right: 17%;
color: $primary;
position: relative;
}
.dark-mode-changer svg path {
fill: #a3a9b5!important;
}
.dropdown-toggle::after {
margin-left: 0.51em!important;
color: #a3a9b5;
}
#toggleImage2 {
display: none;
outline: none;
padding-left: 5px;
padding-right: 5px;
}
#toggleImage1 {
width: 27px!important;
}
.dark-mode-changer {
display: none!important;
}
}

@ -280,6 +280,9 @@ $network-selector-item-icon-dimensions: 30px !default;
&-lukso-l14-testnet {
background-image: url(/images/network-selector-icons/lukso-l14-testnet.svg)
}
&-xusdt-chain {
background-image: url(/images/network-selector-icons/circle-xusdt.svg)
}
}
.network-selector-item-title {

@ -562,3 +562,15 @@ $cube-quantity: 5;
transform: scale(0);
}
}
.dark-block-loader {
width: auto;
height: 15px;
background-color: #e2e5ec;
margin-bottom: 5px;
border-radius: 4px;
}
.dark-theme-applied .dark-block-loader {
background-color: #313355;
}

@ -1,23 +1,23 @@
// general
$primary: #17314f;
$secondary: #15bba6;
$tertiary: #93d7ff;
$primary: #233174;
$secondary: #15f9bb;
$tertiary: #5a77ff;
$additional-font: #fff;
// footer
$footer-background-color: $primary;
$footer-background-color:#202d6a;
$footer-title-color: #fff;
$footer-text-color: #96bde8;
$footer-text-color: #b5c2ff;
$footer-item-disc-color: $secondary;
.footer-logo { filter: brightness(0) invert(1); }
.footer-social-icon { color: $secondary!important; }
// dashboard
$dashboard-line-color-price: $tertiary; // price left border
$dashboard-line-color-market: $secondary;
$dashboard-banner-chart-legend-label-color: $footer-text-color;
$dashboard-stats-item-label-color: $footer-text-color;
$dashboard-stats-item-label-color: #b4c1ff;
$dashboard-banner-chart-legend-value-color: #fff; // chart labels
$dashboard-stats-item-value-color: #fff; // stat values
@ -25,9 +25,11 @@ $dashboard-stats-item-border-color: $secondary; // stat border
$dashboard-banner-gradient-start: $primary; // gradient begin
$dashboard-banner-gradient-end: lighten($primary, 5); // gradient end
$dashboard-banner-gradient-end: #1e2a63;
// gradient end
$dashboard-banner-network-plain-container-background-color: #20446e; // stats bg
$dashboard-banner-network-plain-container-background-color: #273781
; // stats bg
// navigation
@ -35,20 +37,20 @@ $dashboard-banner-network-plain-container-background-color: #20446e; // stats bg
// buttons
$btn-line-bg: #fff; // button bg
$btn-line-color: $secondary; // button border and font color && hover bg color
$btn-copy-color: $secondary; // btn copy
$btn-qr-color: $secondary; // btn qr-code
$btn-line-color: $primary; // button border and font color && hover bg color
$btn-copy-color: $primary; // btn copy
$btn-qr-color: $primary; // btn qr-code
$btn-address-card-icon-color: $secondary; // btn address color
//links & tile
$tile-body-a-color: $secondary;
$tile-type-block-color: $secondary;
$tile-type-progress-bar-color: $secondary;
a.tile-title { color: $secondary !important; }
$tile-body-a-color: $tertiary;
$tile-type-block-color: $primary;
$tile-type-progress-bar-color: $primary;
a.tile-title { color: $primary !important; }
// card
$card-background-1: $secondary;
$card-tab-active: $secondary;
$card-background-1: $primary;
$card-tab-active: $primary;
.layout-container {
.dashboard-banner-container {
@ -66,6 +68,20 @@ $badge-neutral-background-color: rgba(#20446e, .1);
$api-text-monospace-color: #20446e;
// Dark theme
$dark-primary: #15bba6;
$dark-secondary: #93d7ff;
$dark-primary-alternate: #15bba6;
$dark-primary: #233174;
$dark-secondary:#15f9bb;
$dark-tertiary: #5a77ff;
.dark-theme-applied .tile .tile-body a, .dark-theme-applied .tile span[data-address-hash] {
color: $dark-tertiary!important;
}
.dark-theme-applied .btn-line {
background-color: transparent!important;
border-color: $dark-tertiary!important;
color: $dark-tertiary!important;
}
.dark-theme-applied .btn-line:hover {
color: $additional-font!important;
}

@ -778,4 +778,49 @@ $labels-dark: #8a8dba; // header nav, labels
color: #fff;
}
}
}
}
.navbar-dark .navbar-toggler {
color: rgba(255, 255, 255, 0.5);
border: none;
}
.dark-theme-applied .dropdown-item {
color: #fff!important;
}
.dark-theme-applied .dropdown-item.active, .dark-theme-applied .dropdown-item:hover, .dark-theme-applied .dropdown-item:focus {
background-image: none;
width: 100%;
background-color: #35335d!important;
}
.dark-theme-applied .dropdown-item:hover:before {
content: "|";
height: 50px;
width: 50%;
opacity: 1;
background: none;
right: 17%;
color: $dark-primary;
position: relative;
}
.dark-theme-applied .navbar.navbar-primary .navbar-nav .nav-link:hover,
.dark-theme-applied .navbar.navbar-primary .navbar-nav .nav-link.activeLink,
.dark-theme-applied .navbar.navbar-primary .navbar-nav .nav-link:focus {
background-image: none;
width: 100%;
background-color: #35335d!important;
color: white;
border: none;
}
.dark-theme-applied .navbar.navbar-primary .navbar-nav .nav-link:hover:before
{
content: "|";
height: 50px;
width: 50%;
opacity: 1;
background: none;
left: 24%;
top: 14%;
color: $dark-primary;
}

@ -1,5 +1,6 @@
@import "theme/base_variables";
@import "neutral_variables-non-critical";
// @import "xusdt_variables-non-critical";
// @import "dai_variables-non-critical";
// @import "ethereum_classic_variables-non-critical";
// @import "ethereum_variables-non-critical";

@ -1,5 +1,6 @@
@import "theme/base_variables";
@import "neutral_variables";
// @import "xusdt_variables";
// @import "dai_variables";
// @import "ethereum_classic_variables";
// @import "ethereum_variables";

@ -0,0 +1,7 @@
// general
$primary: #2b9f7a;
$secondary: #20745a;
$tertiary: #fff;
$additional-font: #fff;
$btn-line-color: $secondary; // button border and font color && hover bg color

@ -0,0 +1,97 @@
// general
$primary: #2b9f7a;
$secondary: #20745a;
$tertiary: #fff;
$additional-font: #fff;
$tile-body-a-color: $primary;
$tile-type-block-color: $primary;
$tile-type-progress-bar-color: $primary;
a.tile-title { color: $primary !important; }
// footer
$footer-background-color: #282d31;
$footer-title-color: #fff;
$footer-text-color: $additional-font;
$footer-item-disc-color: $secondary;
$footer-social-icon-color: $secondary;
// dashboard
$dashboard-line-color-price: #fff; // price left border
$dashboard-banner-chart-legend-label-color: #fff;
$dashboard-stats-item-label-color: $dashboard-banner-chart-legend-label-color;
$dashboard-banner-chart-legend-value-color: #fff; // chart labels
$dashboard-stats-item-value-color: #fff; // stat values
$dashboard-stats-item-border-color: $secondary; // stat border
$dashboard-banner-gradient-start: $primary; // gradient begin
$dashboard-banner-gradient-end: #289371; // gradient end
$dashboard-banner-network-plain-container-background-color: #2ea780; // stats bg
// navigation
.navbar { box-shadow: 0px 0px 30px 0px rgba(21, 53, 80, 0.12); } // header shadow
$dropdown-menu-item-hover-color: $primary !default;
$dropdown-menu-item-hover-background: rgba($primary, .1) !default;
$header-icon-color-hover: $primary;
$header-icon-border-color-hover: $primary;
// buttons
$btn-line-bg: #fff; // button bg
$btn-line-color: $primary; // button border and font color && hover bg color
$btn-copy-color: $primary; // btn copy
$btn-qr-color: $primary; // btn qr-code
$btn-address-card-icon-color: $primary; // btn address color
//links & tile
$tile-body-a-color: $primary;
$tile-type-block-color: $primary;
$tile-type-progress-bar-color: $primary;
a.tile-title { color: $primary !important; }
// card
$card-background-1: $primary;
$card-tab-active: $primary;
.layout-container {
.dashboard-banner-container {
background-image: linear-gradient(
to bottom,
$dashboard-banner-gradient-start,
$dashboard-banner-gradient-end
);
}
}
// Badges
$badge-neutral-color: $primary;
$badge-neutral-background-color: rgba($primary, .1);
$api-text-monospace-color: $primary;
// Tokens dropdown
.token-balance-dropdown[aria-labelledby="dropdown-tokens"] {
.dropdown-items .dropdown-item:hover {
color: $primary !important;
}
}
// Dark theme
$dark-primary: #2b9f7a;
$dark-secondary: #20745a;
$dark-primary-alternate: #2b9f7a;
.dark-theme-applied .dashboard-banner-chart-legend .dashboard-banner-chart-legend-item:nth-child(1)::before {
background-color: white!important;
}
.dark-theme-applied .dashboard-banner-chart-legend .dashboard-banner-chart-legend-item:nth-child(2)::before {
background-color: $primary!important;
}
.dark-theme-applied .tile .tile-body a, .dark-theme-applied .tile span[data-address-hash] {
color: #2b9f7a;
}

@ -9,3 +9,12 @@ $('.dark-mode-changer').click(function () {
// reload each theme switch
document.location.reload(true)
})
$('.new-button').click(function () {
if (localStorage.getItem('current-color-mode') === 'dark') {
localStorage.setItem('current-color-mode', 'light')
} else {
localStorage.setItem('current-color-mode', 'dark')
}
// reload each theme switch
document.location.reload(true)
})

@ -7,3 +7,40 @@ $(document).click(function (event) {
$('.navbar-toggler').click()
}
})
$(document).ready(function () {
if (matchMedia) {
var mq = window.matchMedia('(max-width: 900px)')
mq.addListener(WidthChange)
WidthChange(mq)
}
function WidthChange (mq) {
if (mq.matches) {
$('#checkIfSmall').removeClass('dropdown-menu')
$('.dropdown-item').removeClass('active')
$('#checkIfSmall1').removeClass('dropdown-menu')
$('#checkIfSmall2').removeClass('dropdown-menu')
} else {
$('#checkIfSmall').addClass('dropdown-menu')
$('#checkIfSmall1').addClass('dropdown-menu')
$('#checkIfSmall2').addClass('dropdown-menu')
}
}
})
var div1 = document.getElementById('toggleImage1')
var div2 = document.getElementById('toggleImage2')
function switchVisible () {
if (!div1) return
if (getComputedStyle(div1).display === 'inline-block') {
div1.style.display = 'none'
div2.style.display = 'block'
} else {
div1.style.display = 'inline-block'
div2.style.display = 'none'
}
}
document
.getElementById('toggleButton')
.addEventListener('click', switchVisible)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">
<path fill="#12AC97" fill-rule="evenodd" d="M0 30V0h30v30H0z"/>
<path fill="#FFF" fill-rule="evenodd" d="M17.172 15L23 22.001h-4.344.775c-.497 0-.926-.229-1.223-.569l-.015.012-2.654-3.187h-.011a.7.7 0 0 0-.528-.243.7.7 0 0 0-.528.243h-.012l-2.653 3.187-.015-.012c-.297.34-.726.569-1.223.569h.774-4.344L12.828 15 6.999 8h3.569l.001-.001c.497 0 .926.229 1.223.569l.014-.012 2.652 3.185h.012c.13.147.315.245.53.245.215 0 .4-.098.53-.245h.011l2.652-3.185.015.012A1.618 1.618 0 0 1 19.432 8h3.569l-5.829 7z"/>
</svg>

After

Width:  |  Height:  |  Size: 584 B

@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">
<path fill="#12AC97" fill-rule="evenodd" d="M30 14.999V30H0V0h30v14.999z"/>
<path fill="#FFF" fill-rule="evenodd" d="M17.172 14.999l5.828 7h-4.344v-.001h.775c-.497 0-.926-.228-1.223-.568l-.015.012-2.655-3.188h-.019a.681.681 0 0 0-.519-.246.681.681 0 0 0-.519.246h-.019l-2.655 3.188-.015-.012c-.297.34-.726.568-1.223.568h.775v.001H7l5.828-7-5.828-7h3.569c.497 0 .926.228 1.223.568l.015-.011 2.651 3.184h.019a.682.682 0 0 0 .523.252.682.682 0 0 0 .523-.252h.018l2.652-3.184.015.011c.297-.34.726-.568 1.223-.568H23l-5.828 7z"/>
<path fill="#202D6A" fill-rule="evenodd" d="M0 30V0h30v30H0z"/>
<path fill="#15F9BB" fill-rule="evenodd" d="M17.172 15L23 22.001h-4.344.775c-.497 0-.926-.229-1.223-.569l-.015.012-2.654-3.187h-.011a.7.7 0 0 0-.528-.243.7.7 0 0 0-.528.243h-.012l-2.653 3.187-.015-.012c-.297.34-.726.569-1.223.569h.774-4.344L12.828 15 6.999 8h3.569l.001-.001c.497 0 .926.229 1.223.569l.014-.012 2.652 3.185h.012c.13.147.315.245.53.245.215 0 .4-.098.53-.245h.011l2.652-3.185.015.012A1.618 1.618 0 0 1 19.432 8h3.569l-5.829 7z"/>
</svg>

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 587 B

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="101" height="36">
<path fill="#FFF" fill-rule="evenodd" d="M29.079 27.848l-10.126 6.457c-3.496 2.231-3.496 2.231-6.989.003L1.81 27.833C.003 26.553.003 24.511.003 24.511V11.464s0-2.222 1.823-3.337l10.138-6.465c3.493-2.225 3.493-2.225 6.992.005l10.123 6.455c1.833 1.177 1.831 3.338 1.831 3.338v13.052s.002 2.139-1.831 3.336zM19.45 11.883c-.429 0-.8.204-1.057.508l-.013-.01-2.294 2.85h-.01a.611.611 0 0 1-.45.201.608.608 0 0 1-.45-.201h-.027l-2.294-2.85-.012.01a1.381 1.381 0 0 0-1.058-.508H8.697l5.042 6.264-5.042 6.264h3.088c.43 0 .801-.204 1.058-.509l.013.011 2.281-2.834h.019a.608.608 0 0 1 .47-.23c.192 0 .356.093.469.23h.003l2.282 2.834.012-.011c.258.305.629.509 1.058.509h3.088l-5.041-6.264 5.041-6.264H19.45zM99.961 30.953l-2.563-3.675h-.028l.018.206c.033.39.05.748.05 1.072v2.397h-.774v-4.611h1.034l2.556 3.655h.021a19.955 19.955 0 0 1-.029-.528 16.461 16.461 0 0 1-.021-.712v-2.415h.781v4.611h-1.045zM97.22 20.801h-2.952V8.776h-3.78V6.199H101v2.577h-3.78v12.025zm-6.64 10.152l-.521-1.281h-1.992l-.51 1.281h-.898l1.949-4.63h.927l1.95 4.63h-.905zm-1.234-3.186a7.578 7.578 0 0 1-.148-.397 7.769 7.769 0 0 1-.13-.398 6.502 6.502 0 0 1-.281.849l-.471 1.202h1.519l-.489-1.256zm-7.989-6.966h-3.941V6.199h4.37c2.272 0 4.037.63 5.294 1.888 1.257 1.259 1.885 3.016 1.885 5.274 0 2.403-.652 4.244-1.957 5.523-1.304 1.278-3.188 1.917-5.651 1.917zm.581-12.065h-1.571v9.508h1.266c2.844 0 4.266-1.601 4.266-4.804 0-3.136-1.32-4.704-3.961-4.704zm-3.57 18.463a5.145 5.145 0 0 0-.558-.194 2.277 2.277 0 0 0-.618-.083c-.49 0-.875.153-1.155.46-.281.308-.421.731-.421 1.271 0 .567.135.994.405 1.284.269.291.66.436 1.171.436.221 0 .435-.02.641-.059a8.21 8.21 0 0 0 .645-.15v.647a4.36 4.36 0 0 1-1.39.205c-.763 0-1.348-.205-1.757-.613-.409-.409-.613-.995-.613-1.756 0-.479.099-.899.297-1.258a2 2 0 0 1 .861-.827c.376-.191.817-.287 1.323-.287a3.63 3.63 0 0 1 1.475.297l-.306.627zm-8.746-15.851c.264.19.887.531 1.871 1.024 1.301.652 2.193 1.307 2.676 1.962.482.656.723 1.46.723 2.412 0 1.319-.452 2.357-1.356 3.116-.905.759-2.163 1.139-3.776 1.139-1.485 0-2.799-.293-3.942-.879v-2.876c.94.439 1.735.749 2.386.929.65.179 1.245.269 1.785.269.647 0 1.144-.13 1.49-.389.346-.26.519-.646.519-1.159 0-.286-.076-.541-.229-.764a2.598 2.598 0 0 0-.671-.644c-.295-.206-.897-.536-1.804-.989-.851-.419-1.489-.822-1.914-1.208a4.513 4.513 0 0 1-1.019-1.349c-.254-.512-.381-1.112-.381-1.797 0-1.292.418-2.308 1.252-3.047.835-.739 1.989-1.108 3.461-1.108.724 0 1.414.09 2.071.269.657.18 1.344.433 2.062.759l-.952 2.407c-.743-.319-1.357-.542-1.843-.669a5.687 5.687 0 0 0-1.433-.19c-.558 0-.987.137-1.285.41-.299.273-.448.629-.448 1.069 0 .273.061.511.181.714.121.203.313.399.576.589zm-7.969 14.994h.851v3.964h2.207v.647h-3.058v-4.611zm-1.071-5.99c-.87.433-1.898.649-3.085.649-1.79 0-3.18-.481-4.171-1.443-.99-.962-1.485-2.279-1.485-3.95V6.199h2.942v8.939c0 1.126.216 1.951.648 2.477.431.526 1.145.789 2.142.789.965 0 1.665-.264 2.099-.794.435-.529.653-1.36.653-2.492V6.199h2.942v9.449c0 1.078-.23 2.024-.691 2.836-.46.813-1.125 1.435-1.994 1.868zm-6.7 10.601h-.905l-.521-1.281h-1.992l-.51 1.281h-.898l1.95-4.63h.926l1.95 4.63zm-2.286-3.583a8.04 8.04 0 0 1-.131-.398 6.502 6.502 0 0 1-.281.849l-.471 1.202h1.519l-.489-1.256a7.792 7.792 0 0 1-.147-.397zm-4.811-6.569l-2.237-3.825-2.247 3.825h-3.295l3.609-5.703-3.428-5.463h3.295l2.066 3.556 2.085-3.556h3.294l-3.465 5.463 3.627 5.703h-3.304zm-6.277 9.578c.338 0 .587-.057.747-.17a.534.534 0 0 0 .241-.458.55.55 0 0 0-.221-.439c-.148-.119-.452-.261-.913-.425-.475-.17-.81-.365-1.005-.583a1.145 1.145 0 0 1-.292-.789c0-.385.154-.688.463-.909.309-.22.724-.331 1.244-.331.499 0 .996.097 1.49.291l-.271.614c-.463-.172-.877-.258-1.24-.258-.276 0-.485.053-.628.159a.503.503 0 0 0-.213.422.54.54 0 0 0 .085.307.837.837 0 0 0 .282.241c.13.076.366.176.706.3.382.141.662.272.841.394.178.122.309.26.392.413.083.154.125.335.125.543 0 .41-.168.731-.503.965-.335.233-.797.35-1.387.35-.589 0-1.071-.081-1.447-.243v-.713c.238.099.49.177.758.234.267.056.516.085.746.085zm7.306-3.391h-1.533v3.965h-.852v-3.965h-1.533v-.646h3.918v.646zm10.991-.369c.319.185.479.477.479.877a.982.982 0 0 1-.235.672c-.157.179-.383.291-.677.337v.032c.366.061.635.181.807.361.172.18.258.423.258.73 0 .415-.163.739-.49.973-.327.235-.781.352-1.363.352h-1.882v-4.611h1.547c.717 0 1.236.092 1.556.277zm-2.251 3.7h.905c.356 0 .626-.061.807-.181.182-.121.273-.313.273-.573 0-.24-.093-.417-.278-.533-.185-.116-.467-.173-.845-.173h-.862v1.46zm0-2.072h.82c.356 0 .616-.05.78-.15.164-.1.246-.27.246-.509 0-.217-.088-.374-.265-.471-.178-.096-.458-.145-.843-.145h-.738v1.275zm13.135-1.268h-2.096v1.255h1.964v.631h-1.964v1.448h2.096v.64h-2.948v-4.611h2.948v.637zm11.896 1.249h2.402v-1.886h.856v4.611h-.856v-2.079h-2.402v2.079h-.852v-4.611h.852v1.886zm12.572 2.725h-.852v-4.611h.852v4.611z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="101" height="36">
<path fill="#12AC97" fill-rule="evenodd" d="M29.079 27.848l-10.126 6.457c-3.496 2.231-3.496 2.231-6.989.003L1.81 27.833C.003 26.553.003 24.511.003 24.511V11.464s0-2.222 1.823-3.337l10.138-6.465c3.493-2.225 3.493-2.225 6.992.005l10.123 6.455c1.833 1.177 1.831 3.338 1.831 3.338v13.052s.002 2.139-1.831 3.336zM19.45 11.883c-.429 0-.8.204-1.057.508l-.013-.01-2.294 2.85h-.01a.611.611 0 0 1-.45.201.608.608 0 0 1-.45-.201h-.027l-2.294-2.85-.012.01a1.381 1.381 0 0 0-1.058-.508H8.697l5.042 6.264-5.042 6.264h3.088c.43 0 .801-.204 1.058-.509l.013.011 2.281-2.834h.019a.608.608 0 0 1 .47-.23c.192 0 .356.093.469.23h.003l2.282 2.834.012-.011c.258.305.629.509 1.058.509h3.088l-5.041-6.264 5.041-6.264H19.45z"/>
<path fill="#333" fill-rule="evenodd" d="M99.961 30.953l-2.563-3.675h-.028l.018.206c.033.39.05.748.05 1.072v2.397h-.774v-4.611h1.034l2.556 3.655h.021a19.955 19.955 0 0 1-.029-.528 16.461 16.461 0 0 1-.021-.712v-2.415h.781v4.611h-1.045zM97.22 20.801h-2.952V8.776h-3.78V6.199H101v2.577h-3.78v12.025zm-6.64 10.152l-.521-1.281h-1.992l-.51 1.281h-.898l1.949-4.63h.927l1.95 4.63h-.905zm-1.234-3.186a7.578 7.578 0 0 1-.148-.397 7.769 7.769 0 0 1-.13-.398 6.502 6.502 0 0 1-.281.849l-.471 1.202h1.519l-.489-1.256zm-7.989-6.966h-3.941V6.199h4.37c2.272 0 4.037.63 5.294 1.888 1.257 1.259 1.885 3.016 1.885 5.274 0 2.403-.652 4.244-1.957 5.523-1.304 1.278-3.188 1.917-5.651 1.917zm.581-12.065h-1.571v9.508h1.266c2.844 0 4.266-1.601 4.266-4.804 0-3.136-1.32-4.704-3.961-4.704zm-3.57 18.463a5.145 5.145 0 0 0-.558-.194 2.277 2.277 0 0 0-.618-.083c-.49 0-.875.153-1.155.46-.281.308-.421.731-.421 1.271 0 .567.135.994.405 1.284.269.291.66.436 1.171.436.221 0 .435-.02.641-.059a8.21 8.21 0 0 0 .645-.15v.647a4.36 4.36 0 0 1-1.39.205c-.763 0-1.348-.205-1.757-.613-.409-.409-.613-.995-.613-1.756 0-.479.099-.899.297-1.258a2 2 0 0 1 .861-.827c.376-.191.817-.287 1.323-.287a3.63 3.63 0 0 1 1.475.297l-.306.627zm-8.746-15.851c.264.19.887.531 1.871 1.024 1.301.652 2.193 1.307 2.676 1.962.482.656.723 1.46.723 2.412 0 1.319-.452 2.357-1.356 3.116-.905.759-2.163 1.139-3.776 1.139-1.485 0-2.799-.293-3.942-.879v-2.876c.94.439 1.735.749 2.386.929.65.179 1.245.269 1.785.269.647 0 1.144-.13 1.49-.389.346-.26.519-.646.519-1.159 0-.286-.076-.541-.229-.764a2.598 2.598 0 0 0-.671-.644c-.295-.206-.897-.536-1.804-.989-.851-.419-1.489-.822-1.914-1.208a4.513 4.513 0 0 1-1.019-1.349c-.254-.512-.381-1.112-.381-1.797 0-1.292.418-2.308 1.252-3.047.835-.739 1.989-1.108 3.461-1.108.724 0 1.414.09 2.071.269.657.18 1.344.433 2.062.759l-.952 2.407c-.743-.319-1.357-.542-1.843-.669a5.687 5.687 0 0 0-1.433-.19c-.558 0-.987.137-1.285.41-.299.273-.448.629-.448 1.069 0 .273.061.511.181.714.121.203.313.399.576.589zm-7.969 14.994h.851v3.964h2.207v.647h-3.058v-4.611zm-1.071-5.99c-.87.433-1.898.649-3.085.649-1.79 0-3.18-.481-4.171-1.443-.99-.962-1.485-2.279-1.485-3.95V6.199h2.942v8.939c0 1.126.216 1.951.648 2.477.431.526 1.145.789 2.142.789.965 0 1.665-.264 2.099-.794.435-.529.653-1.36.653-2.492V6.199h2.942v9.449c0 1.078-.23 2.024-.691 2.836-.46.813-1.125 1.435-1.994 1.868zm-6.7 10.601h-.905l-.521-1.281h-1.992l-.51 1.281h-.898l1.95-4.63h.926l1.95 4.63zm-2.286-3.583a8.04 8.04 0 0 1-.131-.398 6.502 6.502 0 0 1-.281.849l-.471 1.202h1.519l-.489-1.256a7.792 7.792 0 0 1-.147-.397zm-4.811-6.569l-2.237-3.825-2.247 3.825h-3.295l3.609-5.703-3.428-5.463h3.295l2.066 3.556 2.085-3.556h3.294l-3.465 5.463 3.627 5.703h-3.304zm-6.277 9.578c.338 0 .587-.057.747-.17a.534.534 0 0 0 .241-.458.55.55 0 0 0-.221-.439c-.148-.119-.452-.261-.913-.425-.475-.17-.81-.365-1.005-.583a1.145 1.145 0 0 1-.292-.789c0-.385.154-.688.463-.909.309-.22.724-.331 1.244-.331.499 0 .996.097 1.49.291l-.271.614c-.463-.172-.877-.258-1.24-.258-.276 0-.485.053-.628.159a.503.503 0 0 0-.213.422.54.54 0 0 0 .085.307.837.837 0 0 0 .282.241c.13.076.366.176.706.3.382.141.662.272.841.394.178.122.309.26.392.413.083.154.125.335.125.543 0 .41-.168.731-.503.965-.335.233-.797.35-1.387.35-.589 0-1.071-.081-1.447-.243v-.713c.238.099.49.177.758.234.267.056.516.085.746.085zm7.306-3.391h-1.533v3.965h-.852v-3.965h-1.533v-.646h3.918v.646zm10.991-.369c.319.185.479.477.479.877a.982.982 0 0 1-.235.672c-.157.179-.383.291-.677.337v.032c.366.061.635.181.807.361.172.18.258.423.258.73 0 .415-.163.739-.49.973-.327.235-.781.352-1.363.352h-1.882v-4.611h1.547c.717 0 1.236.092 1.556.277zm-2.251 3.7h.905c.356 0 .626-.061.807-.181.182-.121.273-.313.273-.573 0-.24-.093-.417-.278-.533-.185-.116-.467-.173-.845-.173h-.862v1.46zm0-2.072h.82c.356 0 .616-.05.78-.15.164-.1.246-.27.246-.509 0-.217-.088-.374-.265-.471-.178-.096-.458-.145-.843-.145h-.738v1.275zm13.135-1.268h-2.096v1.255h1.964v.631h-1.964v1.448h2.096v.64h-2.948v-4.611h2.948v.637zm11.896 1.249h2.402v-1.886h.856v4.611h-.856v-2.079h-2.402v2.079h-.852v-4.611h.852v1.886zm12.572 2.725h-.852v-4.611h.852v4.611z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.AddressCoinBalanceView
@ -60,12 +60,14 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
def index(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(conn, "index.html",
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash),
transaction_count: transaction_count,
validation_count: validation_count,
current_path: current_path(conn)
)
else

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressContractController do
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
@ -20,14 +20,16 @@ defmodule BlockScoutWeb.AddressContractController do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
transaction_count: transaction_count,
validation_count: validation_count
)
else
:error ->

@ -69,6 +69,34 @@ defmodule BlockScoutWeb.AddressController do
redirect(conn, to: address_transaction_path(conn, :index, id))
end
def transaction_and_validation_count(%Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash) do
transaction_count_task =
Task.async(fn ->
transaction_count(address_hash)
end)
validation_count_task =
Task.async(fn ->
validation_count(address_hash)
end)
[transaction_count_task, validation_count_task]
|> Task.yield_many(:timer.seconds(30))
|> Enum.map(fn {_task, res} ->
case res do
{:ok, result} ->
result
{:exit, reason} ->
raise "Query fetching address counters terminated: #{inspect(reason)}"
nil ->
raise "Query fetching address counters timed out."
end
end)
|> List.to_tuple()
end
def transaction_count(%Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash) do
Chain.total_transactions_sent_by_address(address_hash)
end

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressDecompiledContractController do
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
@ -10,14 +10,16 @@ defmodule BlockScoutWeb.AddressDecompiledContractController do
def index(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_decompiled_contract_address(address_hash) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
transaction_count: transaction_count,
validation_count: validation_count
)
else
:error ->

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.InternalTransactionView
@ -63,6 +63,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
def index(conn, %{"address_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
@ -71,8 +73,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
current_path: current_path(conn),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
transaction_count: transaction_count,
validation_count: validation_count
)
else
:error ->

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.AddressLogsController do
Manages events logs tab.
"""
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.AddressLogsView
@ -56,6 +56,8 @@ defmodule BlockScoutWeb.AddressLogsController do
def index(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
@ -63,8 +65,8 @@ defmodule BlockScoutWeb.AddressLogsController do
current_path: current_path(conn),
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
transaction_count: transaction_count,
validation_count: validation_count
)
else
_ ->

@ -12,7 +12,7 @@ defmodule BlockScoutWeb.AddressReadContractController do
alias Explorer.ExchangeRates.Token
alias Indexer.Fetcher.CoinBalanceOnDemand
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
def index(conn, %{"address_id" => address_hash_string}) do
address_options = [
@ -27,14 +27,16 @@ defmodule BlockScoutWeb.AddressReadContractController do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
transaction_count: transaction_count,
validation_count: validation_count
)
else
:error ->

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressTokenController do
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
import BlockScoutWeb.Chain, only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1]
alias BlockScoutWeb.AddressTokenView
@ -57,6 +57,8 @@ defmodule BlockScoutWeb.AddressTokenController do
def index(conn, %{"address_id" => address_hash_string} = _params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
@ -64,8 +66,8 @@ defmodule BlockScoutWeb.AddressTokenController do
current_path: current_path(conn),
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
transaction_count: transaction_count,
validation_count: validation_count
)
else
:error ->

@ -7,7 +7,7 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
import BlockScoutWeb.Chain,
only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1]
@ -77,6 +77,8 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
{: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
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
@ -85,8 +87,8 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
current_path: current_path(conn),
token: token,
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
transaction_count: transaction_count,
validation_count: validation_count
)
else
:error ->

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.AddressTransactionController do
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias BlockScoutWeb.TransactionView
@ -96,6 +96,8 @@ defmodule BlockScoutWeb.AddressTransactionController do
def index(conn, %{"address_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
@ -103,8 +105,8 @@ defmodule BlockScoutWeb.AddressTransactionController do
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash),
transaction_count: transaction_count,
validation_count: validation_count,
current_path: current_path(conn)
)
else

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.AddressValidationController do
"""
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.AddressController, only: [transaction_and_validation_count: 1]
import BlockScoutWeb.Chain,
only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
@ -69,14 +69,16 @@ defmodule BlockScoutWeb.AddressValidationController do
def index(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_or_insert_address_from_hash(address_hash) do
{transaction_count, validation_count} = transaction_and_validation_count(address_hash)
render(
conn,
"index.html",
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
current_path: current_path(conn),
transaction_count: transaction_count(address.hash),
validation_count: validation_count(address.hash),
transaction_count: transaction_count,
validation_count: validation_count,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null()
)
else

@ -7,6 +7,7 @@ defmodule BlockScoutWeb.Notifier do
alias BlockScoutWeb.{AddressContractVerificationView, Endpoint}
alias Explorer.{Chain, Market, Repo}
alias Explorer.Chain.{Address, InternalTransaction, Transaction}
alias Explorer.Chain.Supply.RSK
alias Explorer.Counters.AverageBlockTime
alias Explorer.ExchangeRates.Token
alias Explorer.SmartContract.{Solidity.CodeCompiler, Solidity.CompilerVersion}
@ -76,8 +77,17 @@ defmodule BlockScoutWeb.Notifier do
data -> data
end
exchange_rate_with_available_supply =
case Application.get_env(:explorer, :supply) do
RSK ->
%{exchange_rate | available_supply: nil, market_cap_usd: RSK.market_cap(exchange_rate)}
_ ->
exchange_rate
end
Endpoint.broadcast("exchange_rate:new_rate", "new_rate", %{
exchange_rate: exchange_rate,
exchange_rate: exchange_rate_with_available_supply,
market_history_data: Enum.map(market_history_data, fn day -> Map.take(day, [:closing_price, :date]) end)
})
end

@ -66,7 +66,7 @@
</button>
</div>
<div class="tile tile-muted mb-4">
<pre class="pre-scrollable line-numbers" data-activate-highlight><code class="solidity"><%= for {line, number} <- contract_lines_with_index(@address.smart_contract.contract_source_code) do %><div data-line-number="<%= number %>"><%= line %></div><% end %></code></pre>
<pre class="pre-scrollable line-numbers" data-activate-highlight><code class="solidity"><%= for {line, number} <- contract_lines_with_index(@address.smart_contract.contract_source_code, @address.smart_contract.inserted_at) do %><div data-line-number="<%= number %>"><%= line %></div><% end %></code></pre>
</div>
</section>

@ -91,13 +91,50 @@
<%= gettext "Something went wrong, click to reload." %>
</span>
</button>
<div hidden data-selector="loading-message" class="tile tile-muted text-center mt-3 w-100" >
<span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading...") %>
<div hidden data-selector="loading-message" class="col-lg-3" >
<div data-selector="chain-block">
<div class="tile tile-type-block n-p d-flex flex-column">
<a class="tile-title"><span class="tile-loader tile-label-loader"></span> </a>
<div class="tile-bottom-contents">
<div class="dark-block-loader"></div>
<div class="dark-block-loader"></div>
</div>
</div>
</div>
</div>
<div hidden data-selector="loading-message" class="col-lg-3" >
<div data-selector="chain-block">
<div class="tile tile-type-block n-p d-flex flex-column">
<a class="tile-title"><span class="tile-loader tile-label-loader"></span> </a>
<div class="tile-bottom-contents">
<div class="dark-block-loader"></div>
<div class="dark-block-loader"></div>
</div>
</div>
</div>
</div>
<div hidden data-selector="loading-message" class="col-lg-3" >
<div data-selector="chain-block">
<div class="tile tile-type-block n-p d-flex flex-column">
<a class="tile-title"><span class="tile-loader tile-label-loader"></span> </a>
<div class="tile-bottom-contents">
<div class="dark-block-loader"></div>
<div class="dark-block-loader"></div>
</div>
</div>
</div>
</div>
<div hidden data-selector="loading-message" class="col-lg-3" >
<div data-selector="chain-block">
<div class="tile tile-type-block n-p d-flex flex-column">
<a class="tile-title"><span class="tile-loader tile-label-loader"></span> </a>
<div class="tile-bottom-contents">
<div class="dark-block-loader"></div>
<div class="dark-block-loader"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -46,6 +46,54 @@
</div>
</div>
</div>
<div data-loading-message data-selector="loading-message" class="tile tile-type-loading">
<div class="row tile-body">
<div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">
<span class="tile-label">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="tile-status-label ml-2 ml-md-0">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
<div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0">
<span class="tile-loader tile-address-loader"></span>
<span class="tile-loader tile-address-loader"></span>
</div>
<div class="col-md-3 col-lg-2 d-flex flex-row flex-md-column flex-nowrap justify-content-center text-md-right mt-3 mt-md-0 tile-bottom">
<span class="mr-2 mr-md-0 order-1">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="mr-2 mr-md-0 order-2">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
</div>
</div>
<div data-loading-message data-selector="loading-message" class="tile tile-type-loading">
<div class="row tile-body">
<div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">
<span class="tile-label">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="tile-status-label ml-2 ml-md-0">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
<div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0">
<span class="tile-loader tile-address-loader"></span>
<span class="tile-loader tile-address-loader"></span>
</div>
<div class="col-md-3 col-lg-2 d-flex flex-row flex-md-column flex-nowrap justify-content-center text-md-right mt-3 mt-md-0 tile-bottom">
<span class="mr-2 mr-md-0 order-1">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="mr-2 mr-md-0 order-2">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
</div>
</div>
<div data-loading-message data-selector="loading-message" class="tile tile-type-loading">
<div class="row tile-body">
<div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">

@ -12,13 +12,27 @@
<%= link to: webapp_url(@conn), class: "navbar-brand", "data-test": "header_logo" do %>
<img class="navbar-logo" id="navbar-logo" src="<%= logo() %>" alt="<%= subnetwork_title() %>" />
<% end %>
<a class="nav-link-new nav-link topnav-nav-link active-icon dropdown-toggle js-show-network-selector" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="6" height="6">
<circle cx="3" cy="3" r="3" fill="#80d6a1"></circle>
</svg> </span>Sokol Testnet</a>
<button class="new-button" id="dark-mode-changer">
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="16">
<path fill="#a3a9b5" fill-rule="evenodd" d="M14.88 11.578a.544.544 0 0 0-.599-.166 5.7 5.7 0 0 1-1.924.321c-3.259 0-5.91-2.632-5.91-5.866 0-1.947.968-3.759 2.59-4.849a.534.534 0 0 0-.225-.97A5.289 5.289 0 0 0 8.059 0C3.615 0 0 3.588 0 8s3.615 8 8.059 8c2.82 0 5.386-1.423 6.862-3.806a.533.533 0 0 0-.041-.616z"></path>
</svg>
</button>
<script>
if (localStorage.getItem("current-color-mode") === "dark") {
document.getElementById("navbar-logo").style.filter = "brightness(0) invert(1)";
}
</script>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="<%= gettext("Toggle navigation") %>">
<span class="navbar-toggler-icon"></span>
<button class="navbar-toggler" id="toggleButton" onclick="switchVisible(); type="button" value="Click" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="<%= gettext("Toggle navigation") %>">
<span class="navbar-toggler-icon" id="toggleImage1" style="width="26px;"></span>
<span class="navbar-toggler-icon-1" id="toggleImage2"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 47.971 47.971" style="enable-background:new 0 0 47.971 47.971; width: 17px;
transform: translate(0px, -1.5px);" xml:space="preserve"> <g><g>
<path d="M28.228,23.986L47.092,5.122c1.172-1.171,1.172-3.071,0-4.242c-1.172-1.172-3.07-1.172-4.242,0L23.986,19.744L5.121,0.88 c-1.172-1.172-3.07-1.172-4.242,0c-1.172,1.171-1.172,3.071,0,4.242l18.865,18.864L0.879,42.85c-1.172,1.171-1.172,3.071,0,4.242 C1.465,47.677,2.233,47.97,3,47.97s1.535-0.293,2.121-0.879l18.865-18.864L42.85,47.091c0.586,0.586,1.354,0.879,2.121,0.879 s1.535-0.293,2.121-0.879c1.172-1.171,1.172-3.071,0-4.242L28.228,23.986z" data-original="#000000" class="active-path" data-old_color="#000000" fill="#a3a9b5"/>
</g></g> </svg></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav">
@ -30,7 +44,7 @@
</span>
<%= gettext("Blocks") %>
</a>
<div class="dropdown-menu" aria-labelledby="navbarBlocksDropdown">
<div class="dropdown-menu" id="checkIfSmall" aria-labelledby="navbarBlocksDropdown">
<%= link to: block_path(@conn, :index), class: "dropdown-item #{tab_status("blocks", @conn.request_path)}" do %>
<%= gettext("Blocks") %>
<% end %>
@ -42,14 +56,14 @@
<% end %>
</div>
</li>
<li class="nav-item dropdown">
<a href="#" role="button" id="navbarTransactionsDropdown" class="nav-link topnav-nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<li class="nav-item dropdown" id="activeTransactions">
<a href="#" role="button" id="navbarTransactionsDropdown1" class="nav-link topnav-nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_transaction_icon.html" %>
</span>
<%= gettext("Transactions") %>
</a>
<div class="dropdown-menu" aria-labeledby="navbarTransactionsDropdown">
<div class="dropdown-menu" id="checkIfSmall1" aria-labeledby="navbarTransactionsDropdown">
<%= link(
gettext("Validated"),
class: "dropdown-item #{tab_status("txs", @conn.request_path)}",
@ -80,7 +94,7 @@
</span>
<%= gettext("APIs") %>
</a>
<div class="dropdown-menu" aria-labeledby="navbarTransactionsDropdown">
<div class="dropdown-menu" id="checkIfSmall2" aria-labeledby="navbarTransactionsDropdown">
<%= link(
gettext("GraphQL"),
class: "dropdown-item #{tab_status("graphiql", @conn.request_path)}",
@ -100,7 +114,7 @@
</li>
<% end %>
<li class="nav-item dropdown">
<a class="nav-link topnav-nav-link active-icon <%= if dropdown_nets() != [], do: "dropdown-toggle js-show-network-selector" %>" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<a class="nav-link last-child topnav-nav-link active-icon <%= if dropdown_nets() != [], do: "dropdown-toggle js-show-network-selector" %>" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_active_icon.html" %>
</span>
@ -108,7 +122,7 @@
</a>
</li>
</ul>
<!-- Dark mode changer -->
<!-- Dark mode changer was there -->
<button class="dark-mode-changer" id="dark-mode-changer">
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="16">
<path fill="#9B62FF" fill-rule="evenodd" d="M14.88 11.578a.544.544 0 0 0-.599-.166 5.7 5.7 0 0 1-1.924.321c-3.259 0-5.91-2.632-5.91-5.866 0-1.947.968-3.759 2.59-4.849a.534.534 0 0 0-.225-.97A5.289 5.289 0 0 0 8.059 0C3.615 0 0 3.588 0 8s3.615 8 8.059 8c2.82 0 5.386-1.423 6.862-3.806a.533.533 0 0 0-.041-.616z"/>

@ -19,7 +19,7 @@
</div>
<% end %>
<input type="submit" value='<%= gettext("Query")%>' class="button button-secondary button-xs py-0 mt-2" />
<input type="submit" value='<%= gettext("Query")%>' class="button btn-line button-xs py-0 mt-2" style="padding: 6px 8px!important;height: 26px;font-size: 11px;" />
</form>
<div class='p-2 text-muted <%= if (queryable?(function["inputs"]) == true), do: "w-100" %>'>

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.AddressContractView do
use BlockScoutWeb, :view
alias ABI.{FunctionSelector, TypeDecoder}
alias Explorer.Chain.{Address, Data, InternalTransaction}
alias Explorer.Chain.{Address, Data, InternalTransaction, SmartContract}
def render("scripts.html", %{conn: conn}) do
render_scripts(conn, "address_contract/code_highlighting.js")
@ -63,8 +63,11 @@ defmodule BlockScoutWeb.AddressContractView do
end)
end
def contract_lines_with_index(contract_source_code) do
contract_lines = String.split(contract_source_code, "\n")
def contract_lines_with_index(source_code, inserted_at \\ nil) do
contract_lines =
source_code
|> String.split("\n")
|> SmartContract.add_submitted_comment(inserted_at)
max_digits =
contract_lines

@ -112,12 +112,12 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:81
#: lib/block_scout_web/templates/layout/_topnav.html.eex:95
msgid "APIs"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:71
#: lib/block_scout_web/templates/layout/_topnav.html.eex:85
msgid "Accounts"
msgstr ""
@ -238,8 +238,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/layout/_topnav.html.eex:31
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/templates/layout/_topnav.html.eex:45
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49
msgid "Blocks"
msgstr ""
@ -683,7 +683,7 @@ msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:95
#: lib/block_scout_web/templates/layout/_topnav.html.eex:109
msgid "Eth RPC"
msgstr ""
@ -749,7 +749,7 @@ msgid "For any existing contracts in the database, insert all ABI entries into t
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:41
#: lib/block_scout_web/templates/layout/_topnav.html.eex:55
msgid "Forked Blocks (Reorgs)"
msgstr ""
@ -787,7 +787,7 @@ msgid "Github"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:85
#: lib/block_scout_web/templates/layout/_topnav.html.eex:99
msgid "GraphQL"
msgstr ""
@ -912,7 +912,7 @@ msgid "To"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
#: lib/block_scout_web/templates/layout/_topnav.html.eex:30
msgid "Toggle navigation"
msgstr ""
@ -1090,7 +1090,6 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:14
#: lib/block_scout_web/templates/chain/show.html.eex:99
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:21
msgid "Loading..."
msgstr ""
@ -1168,7 +1167,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:111
#: lib/block_scout_web/templates/chain/show.html.eex:148
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:10
#: lib/block_scout_web/templates/transaction/index.html.eex:10
msgid "More transactions have come in"
@ -1252,7 +1251,7 @@ msgid "Parent Hash"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:59
#: lib/block_scout_web/templates/layout/_topnav.html.eex:73
#: lib/block_scout_web/views/transaction_view.ex:183
#: lib/block_scout_web/views/transaction_view.ex:217
msgid "Pending"
@ -1288,7 +1287,7 @@ msgid "Query"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:90
#: lib/block_scout_web/templates/layout/_topnav.html.eex:104
msgid "RPC"
msgstr ""
@ -1354,14 +1353,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:127
#: lib/block_scout_web/templates/layout/_topnav.html.eex:144
#: lib/block_scout_web/templates/layout/_topnav.html.eex:141
#: lib/block_scout_web/templates/layout/_topnav.html.eex:158
msgid "Search"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:121
#: lib/block_scout_web/templates/layout/_topnav.html.eex:125
#: lib/block_scout_web/templates/layout/_topnav.html.eex:135
#: lib/block_scout_web/templates/layout/_topnav.html.eex:139
msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
@ -1431,7 +1430,7 @@ msgid "Something went wrong, click to reload."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:117
#: lib/block_scout_web/templates/chain/show.html.eex:154
msgid "Something went wrong, click to retry."
msgstr ""
@ -1595,8 +1594,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_transaction/index.html.eex:15
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10
#: lib/block_scout_web/templates/block_transaction/index.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:108
#: lib/block_scout_web/templates/layout/_topnav.html.eex:50
#: lib/block_scout_web/templates/chain/show.html.eex:145
#: lib/block_scout_web/templates/layout/_topnav.html.eex:64
#: lib/block_scout_web/views/address_view.ex:305
msgid "Transactions"
msgstr ""
@ -1648,7 +1647,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:81
#: lib/block_scout_web/templates/layout/_topnav.html.eex:38
#: lib/block_scout_web/templates/layout/_topnav.html.eex:52
msgid "Uncles"
msgstr ""
@ -1668,7 +1667,7 @@ msgid "Used"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:54
#: lib/block_scout_web/templates/layout/_topnav.html.eex:68
msgid "Validated"
msgstr ""
@ -1732,7 +1731,7 @@ msgid "View All Blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:107
#: lib/block_scout_web/templates/chain/show.html.eex:144
msgid "View All Transactions"
msgstr ""

@ -112,12 +112,12 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:81
#: lib/block_scout_web/templates/layout/_topnav.html.eex:95
msgid "APIs"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:71
#: lib/block_scout_web/templates/layout/_topnav.html.eex:85
msgid "Accounts"
msgstr ""
@ -238,8 +238,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/layout/_topnav.html.eex:31
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/templates/layout/_topnav.html.eex:45
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49
msgid "Blocks"
msgstr ""
@ -683,7 +683,7 @@ msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:95
#: lib/block_scout_web/templates/layout/_topnav.html.eex:109
msgid "Eth RPC"
msgstr ""
@ -749,7 +749,7 @@ msgid "For any existing contracts in the database, insert all ABI entries into t
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:41
#: lib/block_scout_web/templates/layout/_topnav.html.eex:55
msgid "Forked Blocks (Reorgs)"
msgstr ""
@ -787,7 +787,7 @@ msgid "Github"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:85
#: lib/block_scout_web/templates/layout/_topnav.html.eex:99
msgid "GraphQL"
msgstr ""
@ -912,7 +912,7 @@ msgid "To"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
#: lib/block_scout_web/templates/layout/_topnav.html.eex:30
msgid "Toggle navigation"
msgstr ""
@ -1090,7 +1090,6 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:14
#: lib/block_scout_web/templates/chain/show.html.eex:99
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:21
msgid "Loading..."
msgstr ""
@ -1168,7 +1167,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:111
#: lib/block_scout_web/templates/chain/show.html.eex:148
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:10
#: lib/block_scout_web/templates/transaction/index.html.eex:10
msgid "More transactions have come in"
@ -1252,7 +1251,7 @@ msgid "Parent Hash"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:59
#: lib/block_scout_web/templates/layout/_topnav.html.eex:73
#: lib/block_scout_web/views/transaction_view.ex:183
#: lib/block_scout_web/views/transaction_view.ex:217
msgid "Pending"
@ -1288,7 +1287,7 @@ msgid "Query"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:90
#: lib/block_scout_web/templates/layout/_topnav.html.eex:104
msgid "RPC"
msgstr ""
@ -1354,14 +1353,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:127
#: lib/block_scout_web/templates/layout/_topnav.html.eex:144
#: lib/block_scout_web/templates/layout/_topnav.html.eex:141
#: lib/block_scout_web/templates/layout/_topnav.html.eex:158
msgid "Search"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:121
#: lib/block_scout_web/templates/layout/_topnav.html.eex:125
#: lib/block_scout_web/templates/layout/_topnav.html.eex:135
#: lib/block_scout_web/templates/layout/_topnav.html.eex:139
msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
@ -1431,7 +1430,7 @@ msgid "Something went wrong, click to reload."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:117
#: lib/block_scout_web/templates/chain/show.html.eex:154
msgid "Something went wrong, click to retry."
msgstr ""
@ -1595,8 +1594,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_transaction/index.html.eex:15
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10
#: lib/block_scout_web/templates/block_transaction/index.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:108
#: lib/block_scout_web/templates/layout/_topnav.html.eex:50
#: lib/block_scout_web/templates/chain/show.html.eex:145
#: lib/block_scout_web/templates/layout/_topnav.html.eex:64
#: lib/block_scout_web/views/address_view.ex:305
msgid "Transactions"
msgstr ""
@ -1648,7 +1647,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:81
#: lib/block_scout_web/templates/layout/_topnav.html.eex:38
#: lib/block_scout_web/templates/layout/_topnav.html.eex:52
msgid "Uncles"
msgstr ""
@ -1668,7 +1667,7 @@ msgid "Used"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:54
#: lib/block_scout_web/templates/layout/_topnav.html.eex:68
msgid "Validated"
msgstr ""
@ -1732,7 +1731,7 @@ msgid "View All Blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:107
#: lib/block_scout_web/templates/chain/show.html.eex:144
msgid "View All Transactions"
msgstr ""

@ -1,6 +1,6 @@
defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
use BlockScoutWeb.ConnCase
alias Explorer.Factory
alias Explorer.{Chain, Factory}
describe "listcontracts" do
setup do
@ -450,7 +450,9 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
expected_result = [
%{
"Address" => to_string(contract.address_hash),
"SourceCode" => contract.contract_source_code,
"SourceCode" =>
"/**\n* Submitted for verification at blockscout.com on #{contract.inserted_at}\n*/\n" <>
contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
@ -496,9 +498,13 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
|> get("/api", params)
|> json_response(200)
verified_contract = Chain.address_hash_to_smart_contract(contract_address.hash)
expected_result = %{
"Address" => to_string(contract_address.hash),
"SourceCode" => contract_code_info.source_code,
"SourceCode" =>
"/**\n* Submitted for verification at blockscout.com on #{verified_contract.inserted_at}\n*/\n" <>
contract_code_info.source_code,
"ABI" => Jason.encode!(contract_code_info.abi),
"ContractName" => contract_code_info.name,
"CompilerVersion" => contract_code_info.version,
@ -563,8 +569,14 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
result = response["result"]
verified_contract = Chain.address_hash_to_smart_contract(contract_address.hash)
assert result["Address"] == to_string(contract_address.hash)
assert result["SourceCode"] == contract_source_code
assert result["SourceCode"] ==
"/**\n* Submitted for verification at blockscout.com on #{verified_contract.inserted_at}\n*/\n" <>
contract_source_code
assert result["ContractName"] == name
assert result["DecompiledSourceCode"] == "Contract source code not decompiled."
assert result["DecompilerVersion"] == ""

@ -38,22 +38,25 @@ defmodule BlockScoutWeb.AddressContractViewTest do
result = AddressContractView.contract_lines_with_index(code)
assert result == [
{"pragma solidity >=0.4.22 <0.6.0;", " 1"},
{"", " 2"},
{"struct Proposal {", " 3"},
{" uint voteCount;", " 4"},
{"}", " 5"},
{"", " 6"},
{"address chairperson;", " 7"},
{"mapping(address => Voter) voters;", " 8"},
{"Proposal[] proposals;", " 9"},
{"", "10"},
{"constructor(uint8 _numProposals) public {", "11"},
{" chairperson = msg.sender;", "12"},
{" voters[chairperson].weight = 1;", "13"},
{" proposals.length = _numProposals;", "14"},
{"}", "15"},
{"", "16"}
{"/**", " 1"},
{"* Submitted for verification at blockscout.com on ", " 2"},
{"*/", " 3"},
{"pragma solidity >=0.4.22 <0.6.0;", " 4"},
{"", " 5"},
{"struct Proposal {", " 6"},
{" uint voteCount;", " 7"},
{"}", " 8"},
{"", " 9"},
{"address chairperson;", "10"},
{"mapping(address => Voter) voters;", "11"},
{"Proposal[] proposals;", "12"},
{"", "13"},
{"constructor(uint8 _numProposals) public {", "14"},
{" chairperson = msg.sender;", "15"},
{" voters[chairperson].weight = 1;", "16"},
{" proposals.length = _numProposals;", "17"},
{"}", "18"},
{"", "19"}
]
end
@ -66,7 +69,17 @@ defmodule BlockScoutWeb.AddressContractViewTest do
test "returns a list of tuples and the first element is just a line from the original string" do
result = AddressContractView.contract_lines_with_index("a\nb\nc\nd\ne")
assert Enum.map(result, fn {line, _number} -> line end) == ["a", "b", "c", "d", "e"]
assert Enum.map(result, fn {line, _number} -> line end) == [
"/**",
"* Submitted for verification at blockscout.com on ",
"*/",
"a",
"b",
"c",
"d",
"e"
]
end
end
end

@ -61,9 +61,7 @@ defmodule EthereumJSONRPC.Receipts do
"""
@spec elixir_to_logs(elixir) :: Logs.elixir()
def elixir_to_logs(elixir) when is_list(elixir) do
elixir
|> Enum.flat_map(&Receipt.elixir_to_logs/1)
|> Enum.filter(&(Map.get(&1, "type") != "pending"))
Enum.flat_map(elixir, &Receipt.elixir_to_logs/1)
end
@doc """

@ -14,7 +14,7 @@ config :explorer,
System.get_env("ALLOWED_EVM_VERSIONS") ||
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,default",
include_uncles_in_average_block_time:
if(System.get_env("UNCLES_IN_AVERAGE_BLOCK_TIME") == "false", do: false, else: true),
if(System.get_env("UNCLES_IN_AVERAGE_BLOCK_TIME") == "true", do: true, else: false),
healthy_blocks_period: System.get_env("HEALTHY_BLOCKS_PERIOD") || :timer.minutes(5)
average_block_period =
@ -27,11 +27,16 @@ config :explorer, Explorer.Counters.AverageBlockTime,
enabled: true,
period: average_block_period
config :explorer, Explorer.ChainSpec.GenesisData, enabled: false, chain_spec_path: System.get_env("CHAIN_SPEC_PATH")
config :explorer, Explorer.Chain.Cache.BlockNumber, enabled: true
config :explorer, Explorer.ChainSpec.GenesisData,
enabled: true,
chain_spec_path: System.get_env("CHAIN_SPEC_PATH"),
emission_format: System.get_env("EMISSION_FORMAT", "DEFAULT"),
rewards_contract_address: System.get_env("REWARDS_CONTRACT_ADDRESS", "0xeca443e8e1ab29971a45a9c57a6a9875701698a5")
config :explorer, Explorer.ExchangeRates.Source.CoinGecko, coin_id: System.get_env("COIN_GECKO_ID", "poa-network")
config :explorer, Explorer.Chain.Cache.BlockNumber,
enabled: true,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
balances_update_interval =
if System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL") do
@ -123,6 +128,14 @@ market_history_cache_period =
config :explorer, Explorer.Market.MarketHistoryCache, period: market_history_cache_period
config :explorer, Explorer.Chain.Cache.Blocks,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
config :explorer, Explorer.Chain.Cache.Transactions,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"

@ -56,9 +56,7 @@ defmodule Explorer.Application do
opts = [strategy: :one_for_one, name: Explorer.Supervisor]
res = Supervisor.start_link(children, opts)
res
Supervisor.start_link(children, opts)
end
defp configurable_children do

@ -2592,8 +2592,26 @@ defmodule Explorer.Chain do
@spec address_hash_to_address_with_source_code(Hash.Address.t()) :: Address.t() | nil
def address_hash_to_address_with_source_code(address_hash) do
case Repo.get(Address, address_hash) do
nil -> nil
address -> Repo.preload(address, [:smart_contract, :decompiled_smart_contracts])
nil ->
nil
address ->
address_with_smart_contract = Repo.preload(address, [:smart_contract, :decompiled_smart_contracts])
if address_with_smart_contract.smart_contract do
formatted_code =
SmartContract.add_submitted_comment(
address_with_smart_contract.smart_contract.contract_source_code,
address_with_smart_contract.smart_contract.inserted_at
)
%{
address_with_smart_contract
| smart_contract: %{address_with_smart_contract.smart_contract | contract_source_code: formatted_code}
}
else
address_with_smart_contract
end
end
end

@ -28,8 +28,8 @@ defmodule Explorer.Chain.Cache.BlockCount do
defp handle_fallback(:async_task) do
# If this gets called it means an async task was requested, but none exists
# so a new one needs to be launched
task =
Task.async(fn ->
{:ok, task} =
Task.start(fn ->
try do
result = Chain.fetch_count_consensus_block()

@ -7,7 +7,9 @@ defmodule Explorer.Chain.Cache.BlockNumber do
use Explorer.Chain.MapCache,
name: :block_number,
keys: [:min, :max]
keys: [:min, :max],
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval],
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl]
alias Explorer.Chain

@ -11,7 +11,9 @@ defmodule Explorer.Chain.Cache.Blocks do
ids_list_key: "block_numbers",
preload: :transactions,
preload: [miner: :names],
preload: :rewards
preload: :rewards,
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval],
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl]
@type element :: Block.t()

@ -29,8 +29,8 @@ defmodule Explorer.Chain.Cache.TransactionCount do
defp handle_fallback(:async_task) do
# If this gets called it means an async task was requested, but none exists
# so a new one needs to be launched
task =
Task.async(fn ->
{:ok, task} =
Task.start(fn ->
try do
result = Repo.aggregate(Transaction, :count, :hash, timeout: :infinity)

@ -16,7 +16,9 @@ defmodule Explorer.Chain.Cache.Transactions do
token_transfers: :token,
token_transfers: :from_address,
token_transfers: :to_address
]
],
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval],
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl]
@type element :: Transaction.t()

@ -9,7 +9,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
alias Ecto.Adapters.SQL
alias Ecto.{Changeset, Multi, Repo}
alias Explorer.Chain.{Address, Block, Hash, Import, InternalTransaction, Transaction}
alias Explorer.Chain.{Address, Block, Hash, Import, InternalTransaction, Log, TokenTransfer, Transaction}
alias Explorer.Chain.Block.Reward
alias Explorer.Chain.Import.Runner
alias Explorer.Chain.Import.Runner.Address.CurrentTokenBalances
@ -58,6 +58,31 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
where_forked: where_forked
})
end)
|> Multi.run(:lose_consensus, fn repo, _ ->
lose_consensus(repo, ordered_consensus_block_numbers, insert_options)
end)
|> Multi.run(:lose_invalid_neighbour_consensus, fn repo, _ ->
lose_invalid_neighbour_consensus(repo, where_invalid_neighbour, insert_options)
end)
|> Multi.run(:remove_nonconsensus_data, fn repo,
%{
lose_consensus: lost_consensus_blocks,
lose_invalid_neighbour_consensus: lost_consensus_neighbours
} ->
nonconsensus_block_numbers =
(lost_consensus_blocks ++ lost_consensus_neighbours)
|> Enum.map(fn %{number: number} ->
number
end)
|> Enum.sort()
|> Enum.dedup()
remove_nonconsensus_data(
repo,
nonconsensus_block_numbers,
insert_options
)
end)
# MUST be after `:derive_transaction_forks`, which depends on values in `transactions` table
|> Multi.run(:fork_transactions, fn repo, _ ->
fork_transactions(%{
@ -67,12 +92,6 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
where_forked: where_forked
})
end)
|> Multi.run(:lose_consensus, fn repo, _ ->
lose_consensus(repo, ordered_consensus_block_numbers, insert_options)
end)
|> Multi.run(:lose_invalid_neighbour_consensus, fn repo, _ ->
lose_invalid_neighbour_consensus(repo, where_invalid_neighbour, insert_options)
end)
|> Multi.run(:delete_address_token_balances, fn repo, _ ->
delete_address_token_balances(repo, ordered_consensus_block_numbers, insert_options)
end)
@ -342,6 +361,136 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
end
end
defp remove_nonconsensus_data(
repo,
nonconsensus_block_numbers,
insert_options
) do
with {:ok, deleted_token_transfers} <-
remove_nonconsensus_token_transfers(repo, nonconsensus_block_numbers, insert_options),
{:ok, deleted_logs} <- remove_nonconsensus_logs(repo, nonconsensus_block_numbers, insert_options),
{:ok, deleted_internal_transactions} <-
remove_nonconsensus_internal_transactions(repo, nonconsensus_block_numbers, insert_options) do
{:ok,
%{
token_transfers: deleted_token_transfers,
logs: deleted_logs,
internal_transactions: deleted_internal_transactions
}}
end
end
defp remove_nonconsensus_token_transfers(repo, nonconsensus_block_numbers, %{timeout: timeout}) do
ordered_token_transfers =
from(token_transfer in TokenTransfer,
where: token_transfer.block_number in ^nonconsensus_block_numbers,
select: map(token_transfer, [:transaction_hash, :log_index]),
# Enforce TokenTransfer ShareLocks order (see docs: sharelocks.md)
order_by: [
token_transfer.transaction_hash,
token_transfer.log_index
],
lock: "FOR UPDATE"
)
query =
from(token_transfer in TokenTransfer,
select: map(token_transfer, [:transaction_hash, :log_index]),
inner_join: ordered_token_transfer in subquery(ordered_token_transfers),
on:
ordered_token_transfer.transaction_hash ==
token_transfer.transaction_hash and
ordered_token_transfer.log_index == token_transfer.log_index
)
try do
{_count, deleted_token_transfers} = repo.delete_all(query, timeout: timeout)
{:ok, deleted_token_transfers}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, block_numbers: nonconsensus_block_numbers}}
end
end
defp remove_nonconsensus_internal_transactions(repo, nonconsensus_block_numbers, %{timeout: timeout}) do
transaction_query =
from(transaction in Transaction,
where: transaction.block_number in ^nonconsensus_block_numbers,
select: map(transaction, [:hash]),
order_by: transaction.hash
)
ordered_internal_transactions =
from(internal_transaction in InternalTransaction,
inner_join: transaction in subquery(transaction_query),
on: internal_transaction.transaction_hash == transaction.hash,
select: map(internal_transaction, [:transaction_hash, :index]),
# Enforce InternalTransaction ShareLocks order (see docs: sharelocks.md)
order_by: [
internal_transaction.transaction_hash,
internal_transaction.index
],
lock: "FOR UPDATE OF i0"
)
query =
from(internal_transaction in InternalTransaction,
select: map(internal_transaction, [:transaction_hash, :index]),
inner_join: ordered_internal_transaction in subquery(ordered_internal_transactions),
on:
ordered_internal_transaction.transaction_hash == internal_transaction.transaction_hash and
ordered_internal_transaction.index == internal_transaction.index
)
try do
{_count, deleted_internal_transactions} = repo.delete_all(query, timeout: timeout)
{:ok, deleted_internal_transactions}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, block_numbers: nonconsensus_block_numbers}}
end
end
defp remove_nonconsensus_logs(repo, nonconsensus_block_numbers, %{timeout: timeout}) do
transaction_query =
from(transaction in Transaction,
where: transaction.block_number in ^nonconsensus_block_numbers,
select: map(transaction, [:hash]),
order_by: transaction.hash
)
ordered_logs =
from(log in Log,
inner_join: transaction in subquery(transaction_query),
on: log.transaction_hash == transaction.hash,
select: map(log, [:transaction_hash, :index]),
# Enforce Log ShareLocks order (see docs: sharelocks.md)
order_by: [
log.transaction_hash,
log.index
],
lock: "FOR UPDATE OF l0"
)
query =
from(log in Log,
select: map(log, [:transaction_hash, :index]),
inner_join: ordered_log in subquery(ordered_logs),
on: ordered_log.transaction_hash == log.transaction_hash and ordered_log.index == log.index
)
try do
{_count, deleted_logs} = repo.delete_all(query, timeout: timeout)
{:ok, deleted_logs}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, block_numbers: nonconsensus_block_numbers}}
end
end
defp delete_address_token_balances(_, [], _), do: {:ok, []}
defp delete_address_token_balances(repo, ordered_consensus_block_numbers, %{timeout: timeout}) do

@ -26,7 +26,10 @@ defmodule Explorer.Chain.OrderedCache do
preload: [transaction: :hash]
```
Additionally all of the options accepted by `ConCache.start_link/1` can be
provided as well. By default only `ttl_check_interval:` is set (to `false`).
provided as well. Unless specified, only these values have defaults:
- `:ttl_check_interval` is set (to `false`).
- `:callback` is only set if `:ttl_check_interval` is not `false` to call the
`remove_deleted_from_index` function, that removes expired values from the index.
It's also possible, and advised, to override the implementation of the `c:prevails?/2`
and `c:element_to_id/1` callbacks.
@ -131,10 +134,7 @@ defmodule Explorer.Chain.OrderedCache do
max_size = Keyword.get(opts, :max_size, 100)
preloads = Keyword.get(opts, :preloads) || Keyword.get_values(opts, :preload)
concache_params =
opts
|> Keyword.drop([:ids_list_key, :max_size, :preloads, :preload])
|> Keyword.put_new(:ttl_check_interval, false)
concache_params = Keyword.drop(opts, [:ids_list_key, :max_size, :preloads, :preload])
# credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks
quote do
@ -206,6 +206,19 @@ defmodule Explorer.Chain.OrderedCache do
### Updating function
def remove_deleted_from_index({:delete, _cache_pid, id}) do
# simply check with `ConCache.get` because it is faster
if Enum.member?(ids_list(), id) do
ConCache.update(cache_name(), ids_list_key(), fn ids ->
updated_list = List.delete(ids || [], id)
# ids_list is set to never expire
{:ok, %ConCache.Item{value: updated_list, ttl: :infinity}}
end)
end
end
def remove_deleted_from_index(_), do: nil
@impl OrderedCache
def update(elements) when is_nil(elements), do: :ok
@ -217,7 +230,8 @@ defmodule Explorer.Chain.OrderedCache do
|> Enum.sort(&prevails?(&1, &2))
|> merge_and_update(ids || [], max_size())
{:ok, updated_list}
# ids_list is set to never expire
{:ok, %ConCache.Item{value: updated_list, ttl: :infinity}}
end)
end
@ -308,7 +322,21 @@ defmodule Explorer.Chain.OrderedCache do
provided to this function will override the ones set by using the macro
"""
def child_spec(params) do
params = Keyword.merge(unquote(concache_params), params)
# params specified in `use`
merged_params =
unquote(concache_params)
# params specified in `child_spec`
|> Keyword.merge(params)
# `:ttl_check_interval` needs to be specified, defaults to `false`
|> Keyword.put_new(:ttl_check_interval, false)
# if `:ttl_check_interval` is not `false` the expired values need to be
# removed from the cache's index
params =
case merged_params[:ttl_check_interval] do
false -> merged_params
_ -> Keyword.put_new(merged_params, :callback, &remove_deleted_from_index/1)
end
Supervisor.child_spec({ConCache, params}, id: child_id())
end

@ -271,6 +271,45 @@ defmodule Explorer.Chain.SmartContract do
|> add_error(:contract_source_code, error_message(error))
end
def add_submitted_comment(code, inserted_at) when is_binary(code) do
code
|> String.split("\n")
|> add_submitted_comment(inserted_at)
|> Enum.join("\n")
end
def add_submitted_comment(contract_lines, inserted_at) when is_list(contract_lines) do
etherscan_index =
Enum.find_index(contract_lines, fn line ->
String.contains?(line, "Submitted for verification at Etherscan.io")
end)
blockscout_index =
Enum.find_index(contract_lines, fn line ->
String.contains?(line, "Submitted for verification at blockscout.com")
end)
cond do
etherscan_index && blockscout_index ->
List.replace_at(contract_lines, etherscan_index, "*")
etherscan_index && !blockscout_index ->
List.replace_at(
contract_lines,
etherscan_index,
"* Submitted for verification at blockscout.com on #{inserted_at}"
)
!etherscan_index && !blockscout_index ->
header = ["/**", "* Submitted for verification at blockscout.com on #{inserted_at}", "*/"]
header ++ contract_lines
true ->
contract_lines
end
end
defp upsert_contract_methods(%Ecto.Changeset{changes: %{abi: abi}} = changeset) do
ContractMethod.upsert_from_abi(abi, get_field(changeset, :address_hash))

@ -3,18 +3,23 @@ defmodule Explorer.ChainSpec.Parity.Importer do
Imports data from parity chain spec.
"""
require Logger
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Block.{EmissionReward, Range}
alias Explorer.Chain.Hash.Address, as: AddressHash
alias Explorer.Chain.Wei
alias Explorer.ChainSpec.GenesisData
alias Explorer.ChainSpec.POA.Importer, as: PoaEmissionImporter
@max_block_number :infinity
def import_emission_rewards(chain_spec) do
rewards = emission_rewards(chain_spec)
{_, nil} = Repo.delete_all(EmissionReward)
{_, nil} = Repo.insert_all(EmissionReward, rewards)
if Application.get_env(:explorer, GenesisData)[:emission_format] == "POA" do
PoaEmissionImporter.import_emission_rewards()
else
import_rewards_from_chain_spec(chain_spec)
end
end
def import_genesis_coin_balances(chain_spec) do
@ -38,18 +43,37 @@ defmodule Explorer.ChainSpec.Parity.Importer do
Chain.import(params)
end
defp import_rewards_from_chain_spec(chain_spec) do
rewards = emission_rewards(chain_spec)
{_, nil} = Repo.delete_all(EmissionReward)
{_, nil} = Repo.insert_all(EmissionReward, rewards)
end
def genesis_coin_balances(chain_spec) do
accounts = chain_spec["accounts"]
parse_accounts(accounts)
if accounts do
parse_accounts(accounts)
else
Logger.warn(fn -> "No accounts are defined in chain spec" end)
[]
end
end
def emission_rewards(chain_spec) do
rewards = chain_spec["engine"]["Ethash"]["params"]["blockReward"]
rewards
|> parse_hex_numbers()
|> format_ranges()
if rewards do
rewards
|> parse_hex_numbers()
|> format_ranges()
else
Logger.warn(fn -> "No rewards are defined in chain spec" end)
[]
end
end
defp parse_accounts(accounts) do
@ -59,7 +83,7 @@ defmodule Explorer.ChainSpec.Parity.Importer do
end)
|> Stream.map(fn {address, %{"balance" => value}} ->
{:ok, address_hash} = AddressHash.cast(address)
balance = parse_hex_number(value)
balance = parse_number(value)
%{address_hash: address_hash, value: balance}
end)
@ -92,16 +116,22 @@ defmodule Explorer.ChainSpec.Parity.Importer do
defp parse_hex_numbers(rewards) do
Enum.map(rewards, fn {hex_block_number, hex_reward} ->
block_number = parse_hex_number(hex_block_number)
{:ok, reward} = hex_reward |> parse_hex_number() |> Wei.cast()
block_number = parse_number(hex_block_number)
{:ok, reward} = hex_reward |> parse_number() |> Wei.cast()
{block_number, reward}
end)
end
defp parse_hex_number("0x" <> hex_number) do
defp parse_number("0x" <> hex_number) do
{number, ""} = Integer.parse(hex_number, 16)
number
end
defp parse_number(string_number) do
{number, ""} = Integer.parse(string_number, 10)
number
end
end

@ -0,0 +1,89 @@
defmodule Explorer.ChainSpec.POA.Importer do
@moduledoc """
Imports emission reward range for POA chain.
"""
require Logger
alias Explorer.Chain.Wei
alias Explorer.Repo
alias Explorer.SmartContract.Reader
alias Explorer.Chain.Block.{EmissionReward, Range}
alias Explorer.ChainSpec.GenesisData
@block_reward_amount_abi %{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "uint256", "name" => ""}],
"name" => "blockRewardAmount",
"inputs" => [],
"constant" => true
}
@block_reward_amount_params %{"blockRewardAmount" => []}
@emission_funds_amount_abi %{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "uint256", "name" => ""}],
"name" => "emissionFundsAmount",
"inputs" => [],
"constant" => true
}
@emission_funds_amount_params %{"emissionFundsAmount" => []}
@emission_funds_block_start 5_098_087
def import_emission_rewards do
if is_nil(rewards_contract_address()) do
Logger.warn(fn -> "No rewards contract address is defined" end)
else
block_reward = block_reward_amount()
emission_funds = emission_funds_amount()
rewards = [
%{
block_range: %Range{from: 0, to: @emission_funds_block_start},
reward: %Wei{value: block_reward}
},
%{
block_range: %Range{from: @emission_funds_block_start + 1, to: :infinity},
reward: %Wei{value: Decimal.add(block_reward, emission_funds)}
}
]
{_, nil} = Repo.delete_all(EmissionReward)
{_, nil} = Repo.insert_all(EmissionReward, rewards)
end
end
def block_reward_amount do
call_contract(rewards_contract_address(), @block_reward_amount_abi, @block_reward_amount_params)
end
def emission_funds_amount do
call_contract(rewards_contract_address(), @emission_funds_amount_abi, @emission_funds_amount_params)
end
defp rewards_contract_address do
Application.get_env(:explorer, GenesisData)[:rewards_contract_address]
end
defp call_contract(address, abi, params) do
abi = [abi]
method_name =
params
|> Enum.map(fn {key, _value} -> key end)
|> List.first()
Reader.query_contract(address, abi, params)
value =
case Reader.query_contract(address, abi, params) do
%{^method_name => {:ok, [result]}} -> result
_ -> 0
end
Decimal.new(value)
end
end

@ -44,15 +44,41 @@ defmodule Explorer.ExchangeRates.Source.CoinGecko do
@impl Source
def source_url do
"#{base_url()}/coins/#{coin_id()}"
{:ok, id} = coin_id()
"#{base_url()}/coins/#{id}"
end
defp base_url do
config(:base_url) || "https://api.coingecko.com/api/v3"
end
defp coin_id do
Application.get_env(:explorer, __MODULE__)[:coin_id]
def coin_id do
url = "#{base_url()}/coins/list"
symbol = String.downcase(Explorer.coin())
case HTTPoison.get(url, headers()) do
{:ok, %Response{body: body, status_code: 200}} ->
data = decode_json(body)
symbol_data =
Enum.find(data, fn item ->
item["symbol"] == symbol
end)
if symbol_data do
{:ok, symbol_data["id"]}
else
{:error, :not_found}
end
{:ok, %Response{body: body, status_code: status_code}} when status_code in 400..499 ->
{:error, decode_json(body)["error"]}
{:error, %Error{reason: reason}} ->
{:error, reason}
end
end
defp get_btc_price(currency \\ "usd") do

@ -0,0 +1,7 @@
defmodule Explorer.Repo.Migrations.CreateIndexesForBlockNumberInTokenTransfersAndTransactions do
use Ecto.Migration
def change do
create_if_not_exists(index(:token_transfers, [:block_number]))
end
end

@ -11,7 +11,7 @@ defmodule Explorer.Chain.Cache.BlockNumberTest do
end)
end
describe "get_max/1" do
describe "get_max/0" do
test "returns max number" do
insert(:block, number: 5)
@ -19,7 +19,7 @@ defmodule Explorer.Chain.Cache.BlockNumberTest do
end
end
describe "get_min/1" do
describe "get_min/0" do
test "returns min number" do
insert(:block, number: 2)
@ -27,7 +27,7 @@ defmodule Explorer.Chain.Cache.BlockNumberTest do
end
end
describe "get_all/1" do
describe "get_all/0" do
test "returns min and max number" do
insert(:block, number: 6)

@ -7,13 +7,14 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
alias Ecto.Multi
alias Explorer.Chain.Import.Runner.{Blocks, Transactions}
alias Explorer.Chain.{Address, Block, Transaction}
alias Explorer.Chain.{Address, Block, InternalTransaction, Log, Transaction, TokenTransfer}
alias Explorer.Chain
alias Explorer.Repo
describe "run/1" do
setup do
block = insert(:block, consensus: true)
miner = insert(:address)
block = params_for(:block, consensus: true, miner_hash: miner.hash)
timestamp = DateTime.utc_now()
options = %{timestamps: %{inserted_at: timestamp, updated_at: timestamp}}
@ -22,9 +23,11 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
end
test "derive_transaction_forks replaces hash on conflicting (uncle_hash, index)", %{
consensus_block: %Block{hash: block_hash, miner_hash: miner_hash, number: block_number} = consensus_block,
consensus_block: %{hash: block_hash, miner_hash: miner_hash, number: block_number},
options: options
} do
consensus_block = insert(:block, %{hash: block_hash, number: block_number})
transaction =
:transaction
|> insert()
@ -81,7 +84,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
end
test "delete_address_current_token_balances deletes rows with matching block number when consensus is true",
%{consensus_block: %Block{number: block_number} = block, options: options} do
%{consensus_block: %{number: block_number} = block, options: options} do
%Address.CurrentTokenBalance{address_hash: address_hash, token_contract_address_hash: token_contract_address_hash} =
insert(:address_current_token_balance, block_number: block_number)
@ -98,7 +101,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
end
test "delete_address_current_token_balances does not delete rows with matching block number when consensus is false",
%{consensus_block: %Block{number: block_number} = block, options: options} do
%{consensus_block: %{number: block_number} = block, options: options} do
%Address.CurrentTokenBalance{} = insert(:address_current_token_balance, block_number: block_number)
count = 1
@ -113,8 +116,93 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
assert count(Address.CurrentTokenBalance) == count
end
test "remove_nonconsensus_data deletes token transfer rows with matching block number when new consensus block is inserted",
%{consensus_block: %{number: block_number} = block, options: options} do
insert(:block, number: block_number, consensus: true)
%TokenTransfer{transaction_hash: transaction_hash, log_index: log_index} =
insert(:token_transfer, block_number: block_number, transaction: insert(:transaction))
assert count(TokenTransfer) == 1
assert {:ok,
%{
remove_nonconsensus_data: %{
token_transfers: [
%{transaction_hash: ^transaction_hash, log_index: ^log_index}
]
}
}} = run_block_consensus_change(block, true, options)
assert count(TokenTransfer) == 0
end
test "remove_nonconsensus_data does not delete token transfer rows with matching block number when new consensus block wasn't inserted",
%{consensus_block: %{number: block_number} = block, options: options} do
insert(:token_transfer, block_number: block_number, transaction: insert(:transaction))
count = 1
assert count(TokenTransfer) == count
assert {:ok,
%{
remove_nonconsensus_data: %{
token_transfers: []
}
}} = run_block_consensus_change(block, false, options)
assert count(TokenTransfer) == count
end
test "remove_nonconsensus_data deletes nonconsensus logs", %{
consensus_block: %{number: block_number} = block,
options: options
} do
old_block = insert(:block, number: block_number, consensus: true)
forked_transaction = :transaction |> insert() |> with_block(old_block)
%Log{transaction_hash: hash, index: index} = insert(:log, transaction: forked_transaction)
assert count(Log) == 1
assert {:ok,
%{
remove_nonconsensus_data: %{
logs: [
%{transaction_hash: ^hash, index: ^index}
]
}
}} = run_block_consensus_change(block, true, options)
assert count(Log) == 0
end
test "remove_nonconsensus_data deletes nonconsensus internal transactions", %{
consensus_block: %{number: block_number} = block,
options: options
} do
old_block = insert(:block, number: block_number, consensus: true)
forked_transaction = :transaction |> insert() |> with_block(old_block)
%InternalTransaction{index: index, transaction_hash: hash} =
insert(:internal_transaction, index: 0, transaction: forked_transaction)
assert count(InternalTransaction) == 1
assert {:ok,
%{
remove_nonconsensus_data: %{
internal_transactions: [
%{transaction_hash: ^hash, index: ^index}
]
}
}} = run_block_consensus_change(block, true, options)
assert count(InternalTransaction) == 0
end
test "derive_address_current_token_balances inserts rows if there is an address_token_balance left for the rows deleted by delete_address_current_token_balances",
%{consensus_block: %Block{number: block_number} = block, options: options} do
%{consensus_block: %{number: block_number} = block, options: options} do
token = insert(:token)
token_contract_address_hash = token.contract_address_hash
@ -172,7 +260,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
end
test "a non-holder reverting to a holder increases the holder_count",
%{consensus_block: %Block{hash: block_hash, miner_hash: miner_hash, number: block_number}, options: options} do
%{consensus_block: %{hash: block_hash, miner_hash: miner_hash, number: block_number}, options: options} do
token = insert(:token)
token_contract_address_hash = token.contract_address_hash
@ -204,7 +292,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
end
test "a holder reverting to a non-holder decreases the holder_count",
%{consensus_block: %Block{hash: block_hash, miner_hash: miner_hash, number: block_number}, options: options} do
%{consensus_block: %{hash: block_hash, miner_hash: miner_hash, number: block_number}, options: options} do
token = insert(:token)
token_contract_address_hash = token.contract_address_hash
@ -236,7 +324,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
end
test "a non-holder becoming and a holder becoming while a holder becomes a non-holder cancels out and holder_count does not change",
%{consensus_block: %Block{number: block_number} = block, options: options} do
%{consensus_block: %{number: block_number} = block, options: options} do
token = insert(:token)
token_contract_address_hash = token.contract_address_hash
@ -262,7 +350,8 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
# Regression test for https://github.com/poanetwork/blockscout/issues/1644
test "discards neighbouring blocks if they aren't related to the current one because of reorg and/or import timeout",
%{consensus_block: %Block{number: block_number, hash: block_hash, miner_hash: miner_hash}, options: options} do
%{consensus_block: %{number: block_number, hash: block_hash, miner_hash: miner_hash}, options: options} do
insert(:block, %{number: block_number, hash: block_hash})
old_block1 = params_for(:block, miner_hash: miner_hash, parent_hash: block_hash, number: block_number + 1)
new_block1 = params_for(:block, miner_hash: miner_hash, parent_hash: block_hash, number: block_number + 1)
@ -286,7 +375,8 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
# Regression test for https://github.com/poanetwork/blockscout/issues/1911
test "forces block refetch if transaction is re-collated in a different block",
%{consensus_block: %Block{number: block_number, hash: block_hash, miner_hash: miner_hash}, options: options} do
%{consensus_block: %{number: block_number, hash: block_hash, miner_hash: miner_hash}, options: options} do
insert(:block, %{number: block_number, hash: block_hash})
new_block1 = params_for(:block, miner_hash: miner_hash, parent_hash: block_hash, number: block_number + 1)
new_block2 = params_for(:block, miner_hash: miner_hash, parent_hash: new_block1.hash, number: block_number + 2)
@ -365,7 +455,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do
end
defp run_block_consensus_change(
%Block{hash: block_hash, miner_hash: miner_hash, number: block_number},
%{hash: block_hash, miner_hash: miner_hash, number: block_number},
consensus,
options
) do

@ -18,6 +18,46 @@ defmodule Explorer.ExchangeRates.Source.CoinGeckoTest do
}
"""
@coins_list """
[
{
"id": "poa-network",
"symbol": "poa",
"name": "POA Network"
},
{
"id": "poc-chain",
"symbol": "pocc",
"name": "POC Chain"
},
{
"id": "pocket-arena",
"symbol": "poc",
"name": "Pocket Arena"
},
{
"id": "ethereum",
"symbol": "eth",
"name": "Ethereum"
},
{
"id": "rootstock",
"symbol": "rbtc",
"name": "Rootstock RSK"
},
{
"id": "dai",
"symbol": "dai",
"name": "Dai"
},
{
"id": "callisto",
"symbol": "clo",
"name": "Callisto Network"
}
]
"""
describe "format_data/1" do
setup do
bypass = Bypass.open()
@ -62,4 +102,65 @@ defmodule Explorer.ExchangeRates.Source.CoinGeckoTest do
assert [] = CoinGecko.format_data(bad_data)
end
end
describe "coin_id/0" do
setup do
bypass = Bypass.open()
Application.put_env(:explorer, CoinGecko, base_url: "http://localhost:#{bypass.port}")
on_exit(fn ->
Application.put_env(:explorer, :coin, "POA")
end)
{:ok, bypass: bypass}
end
test "fetches poa coin id by default", %{bypass: bypass} do
Bypass.expect(bypass, "GET", "/coins/list", fn conn ->
Conn.resp(conn, 200, @coins_list)
end)
assert CoinGecko.coin_id() == {:ok, "poa-network"}
end
test "fetches eth coin id", %{bypass: bypass} do
Application.put_env(:explorer, :coin, "ETH")
Bypass.expect(bypass, "GET", "/coins/list", fn conn ->
Conn.resp(conn, 200, @coins_list)
end)
assert CoinGecko.coin_id() == {:ok, "ethereum"}
end
test "fetches rbtc coin id", %{bypass: bypass} do
Application.put_env(:explorer, :coin, "RBTC")
Bypass.expect(bypass, "GET", "/coins/list", fn conn ->
Conn.resp(conn, 200, @coins_list)
end)
assert CoinGecko.coin_id() == {:ok, "rootstock"}
end
test "fetches dai coin id", %{bypass: bypass} do
Application.put_env(:explorer, :coin, "DAI")
Bypass.expect(bypass, "GET", "/coins/list", fn conn ->
Conn.resp(conn, 200, @coins_list)
end)
assert CoinGecko.coin_id() == {:ok, "dai"}
end
test "fetches callisto coin id", %{bypass: bypass} do
Application.put_env(:explorer, :coin, "CLO")
Bypass.expect(bypass, "GET", "/coins/list", fn conn ->
Conn.resp(conn, 200, @coins_list)
end)
assert CoinGecko.coin_id() == {:ok, "callisto"}
end
end
end

@ -84,18 +84,6 @@ defmodule Indexer.Block.Fetcher.ReceiptsTest do
"transactionIndex" => "0x0",
"transactionLogIndex" => "0x0",
"type" => "mined"
},
%{
"address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415c",
"blockHash" => nil,
"blockNumber" => nil,
"data" => "0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"logIndex" => "0x1",
"topics" => ["0x600bcf04a13e752d1e3670a5a9f1c21177ca2a93c6f5391d4f1298d098097c22"],
"transactionHash" => "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5",
"transactionIndex" => "0x0",
"transactionLogIndex" => "0x0",
"type" => "pending"
}
],
"logsBloom" =>
@ -158,8 +146,6 @@ defmodule Indexer.Block.Fetcher.ReceiptsTest do
log[:transaction_hash] == "0x43bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5" &&
log[:block_number] == 46147
end)
refute Enum.find(logs, fn log -> log[:type] == "pending" end)
end
end
end

@ -52,10 +52,11 @@ $ export NETWORK=POA
| `TXS_COUNT_CACHE_PERIOD` | | Interval in seconds to restart the task, which calculates the total txs count. | 60 * 60 * 2 | v1.3.9+ | | |
| `ADDRESS_WITH_BALANCES` <br /> `_UPDATE_INTERVAL`| | Interval in seconds to restart the task, which calculates addresses with balances. | 30 * 60 | v1.3.9+ | | |
| `LINK_TO_OTHER_EXPLORERS` | | true/false. If true, links to other explorers are added in the footer | (empty) | v1.3.0+ | | |
| `COINMARKETCAP_PAGES` | | the number of pages on coinmarketcap to list in order to find token's price | 10 | v1.3.10+ | | master |
| `COINMARKETCAP_PAGES` | | the number of pages on coinmarketcap to list in order to find token's price | 10 | v1.3.10+ | | v2.0.4 |
| `SUPPORTED_CHAINS` | | Array of supported chains that displays in the footer and in the chains dropdown. This var was introduced in this PR [#1900](https://github.com/poanetwork/blockscout/pull/1900) and looks like an array of JSON objects. | (empty) | v2.0.0+ | | |
| `BLOCK_COUNT_CACHE_PERIOD ` | | time to live of cache in seconds. This var was introduced in [#1876](https://github.com/poanetwork/blockscout/pull/1876) | 600 | v2.0.0+ | | |
| `ALLOWED_EVM_VERSIONS ` | | the comma-separated list of allowed EVM versions for contracts verification. This var was introduced in [#1964](https://github.com/poanetwork/blockscout/pull/1964) | "homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg" | v2.0.0+ | | |
| `UNCLES_IN_AVERAGE_BLOCK_TIME` | Include or exclude nonconsensus blocks in avg block time calculation. Exclude if `false`. | false | v2.0.1+ | | |
| `AVERAGE_BLOCK_CACHE_PERIOD` | | Update of average block cache, in seconds | 30 minutes | v2.0.2+ | |
| `MARKET_HISTORY_CACHE_PERIOD` | | Update of market history cache, in seconds | 6 hours | v2.0.2+ | |
| `DISABLE_WEBAPP` | | If `true`, endpoints to webapp are hidden (compile-time) | `false` | v2.0.3+ | :white_check_mark: | |
@ -64,5 +65,7 @@ $ export NETWORK=POA
| `DISABLE_INDEXER` | | If `true`, indexer application doesn't run | `false` | v2.0.3+ | :white_check_mark: | |
| `WEBAPP_URL` | | Link to web application instance, e.g. `http://host/path` | (empty) | v2.0.3+ | | |
| `API_URL` | | Link to API instance, e.g. `http://host/path` | (empty) | v2.0.3+ | | |
| `CHAIN_SPEC_PATH` | | Chain specification path (absolute file system path or url) to import block emission reward ranges and genesis account balances from | (empty) | master | | |
| `COIN_GECKO_ID` | | CoinGecko coin id required for fetching an exchange rate | poa-network | master | | |
| `CHAIN_SPEC_PATH` | | Chain specification path (absolute file system path or url) to import block emission reward ranges and genesis account balances from | (empty) | v2.0.4+ | | |
| `COIN_GECKO_ID` | | CoinGecko coin id required for fetching an exchange rate | poa-network | v2.0.4+ | | master |
| `EMISSION_FORMAT` | | Should be set to `POA` if you have block emission indentical to POA Network. This env var is used only if `CHAIN_SPEC_PATH` is set | `STANDARD` | v2.0.4+ | | |
| `REWARDS_CONTRACT_ADDRESS` | | Emission rewards contract address. This env var is used only if `EMISSION_FORMAT` is set to `POA` | `0xeca443e8e1ab29971a45a9c57a6a9875701698a5` | v2.0.4+ | | |

Loading…
Cancel
Save