Merge branch 'master' into ab-fix-response-without-id

pull/2196/head
Victor Baranov 5 years ago committed by GitHub
commit 5eae0fb908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 50
      CHANGELOG.md
  2. 20
      README.md
  3. 6
      apps/block_scout_web/assets/css/_typography.scss
  4. 4
      apps/block_scout_web/assets/css/app.scss
  5. 6
      apps/block_scout_web/assets/css/components/_card.scss
  6. 29
      apps/block_scout_web/assets/css/components/_dashboard-banner.scss
  7. 34
      apps/block_scout_web/assets/css/components/_navbar.scss
  8. 341
      apps/block_scout_web/assets/css/components/_network-selector.scss
  9. 49
      apps/block_scout_web/assets/css/components/_radio.scss
  10. 5
      apps/block_scout_web/assets/css/components/_tile.scss
  11. 60
      apps/block_scout_web/assets/css/components/_token-balance-dropdown.scss
  12. 50
      apps/block_scout_web/assets/css/components/_transaction.scss
  13. 9
      apps/block_scout_web/assets/css/theme/_neutral_variables.scss
  14. 9
      apps/block_scout_web/assets/css/theme/_poa_variables.scss
  15. 4
      apps/block_scout_web/assets/js/app.js
  16. 8
      apps/block_scout_web/assets/js/lib/async_listing_load.js
  17. 2
      apps/block_scout_web/assets/js/lib/currency.js
  18. 77
      apps/block_scout_web/assets/js/lib/network_selector.js
  19. 9
      apps/block_scout_web/assets/js/pages/address/logs.js
  20. 2
      apps/block_scout_web/assets/js/pages/chain.js
  21. 58
      apps/block_scout_web/assets/js/pages/favorites.js
  22. 9
      apps/block_scout_web/assets/js/pages/layout.js
  23. 21
      apps/block_scout_web/assets/js/pages/network-search.js
  24. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/aerum-mainnet.png
  25. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/callisto-mainnet.png
  26. 7
      apps/block_scout_web/assets/static/images/network-selector-icons/callisto.svg
  27. 4
      apps/block_scout_web/assets/static/images/network-selector-icons/core.svg
  28. 33
      apps/block_scout_web/assets/static/images/network-selector-icons/dai.svg
  29. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-classic.png
  30. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-mainnet.png
  31. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/goerli-testnet.png
  32. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/kovan-testnet.png
  33. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/poa-core.png
  34. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/poa-sokol.png
  35. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/rinkeby-testnet.png
  36. 15
      apps/block_scout_web/assets/static/images/network-selector-icons/rinkeby.svg
  37. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/ropsten-testnet.png
  38. 4
      apps/block_scout_web/assets/static/images/network-selector-icons/ropsten.svg
  39. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/rsk-mainnet.png
  40. 4
      apps/block_scout_web/assets/static/images/network-selector-icons/sokol.svg
  41. BIN
      apps/block_scout_web/assets/static/images/network-selector-icons/xdai-chain.png
  42. 2
      apps/block_scout_web/config/config.exs
  43. 11
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
  44. 41
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  45. 2
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  46. 1
      apps/block_scout_web/lib/block_scout_web/controllers/page_not_found_controller.ex
  47. 2
      apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex
  48. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex
  49. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex
  50. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/read_contract_controller.ex
  51. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex
  52. 23
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex
  53. 3
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex
  54. 10
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex
  55. 4
      apps/block_scout_web/lib/block_scout_web/router.ex
  56. 4
      apps/block_scout_web/lib/block_scout_web/templates/address/_responsive_hash.html.eex
  57. 31
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  58. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
  59. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex
  60. 12
      apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex
  61. 6
      apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex
  62. 15
      apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex
  63. 4
      apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex
  64. 2
      apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex
  65. 2
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  66. 53
      apps/block_scout_web/lib/block_scout_web/templates/layout/_network_selector.html.eex
  67. 25
      apps/block_scout_web/lib/block_scout_web/templates/layout/_network_selector_item.html.eex
  68. 21
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  69. 9
      apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
  70. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex
  71. 12
      apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
  72. 10
      apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex
  73. 10
      apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
  74. 15
      apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
  75. 175
      apps/block_scout_web/priv/gettext/default.pot
  76. 177
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  77. 36
      apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs
  78. 11
      apps/block_scout_web/test/block_scout_web/controllers/page_not_found_controller_test.exs
  79. 3
      apps/block_scout_web/test/support/conn_case.ex
  80. 3
      apps/block_scout_web/test/support/feature_case.ex
  81. 4
      apps/explorer/config/config.exs
  82. 26
      apps/explorer/lib/explorer/application.ex
  83. 86
      apps/explorer/lib/explorer/chain.ex
  84. 119
      apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex
  85. 139
      apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex
  86. 3
      apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
  87. 3
      apps/explorer/lib/explorer/chain/import/runner/transactions.ex
  88. 22
      apps/explorer/lib/explorer/chain/smart_contract.ex
  89. 92
      apps/explorer/lib/explorer/chain/supply/rsk.ex
  90. 105
      apps/explorer/lib/explorer/chain/transaction.ex
  91. 143
      apps/explorer/lib/explorer/chain/transactions_cache.ex
  92. 14
      apps/explorer/lib/explorer/counters/average_block_time.ex
  93. 3
      apps/explorer/lib/explorer/smart_contract/publisher.ex
  94. 22
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  95. 7
      apps/explorer/lib/explorer/smart_contract/verifier.ex
  96. 8
      apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
  97. 2
      apps/explorer/mix.exs
  98. 5
      apps/explorer/priv/compile_solc.js
  99. 132
      apps/explorer/priv/repo/migrations/20190619154943_reduce_transaction_status_constraint.exs
  100. 11
      apps/explorer/priv/repo/migrations/20190625085852_add_additional_contract_fields.exs
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,13 +1,42 @@
## Current
### Features
### Fixes
### Chore
## 2.0.1-beta
### Features
- [#2283](https://github.com/poanetwork/blockscout/pull/2283) - Add transactions cache
- [#2182](https://github.com/poanetwork/blockscout/pull/2182) - add market history cache
- [#2109](https://github.com/poanetwork/blockscout/pull/2109) - use bigger updates instead of `Multi` transactions in BlocksTransactionsMismatch
- [#2075](https://github.com/poanetwork/blockscout/pull/2075) - add blocks cache
- [#2151](https://github.com/poanetwork/blockscout/pull/2151) - hide dropdown menu then other networks list is empty
- [#2191](https://github.com/poanetwork/blockscout/pull/2191) - allow to configure token metadata update interval
- [#2146](https://github.com/poanetwork/blockscout/pull/2146) - feat: add eth_getLogs rpc endpoint
- [#2216](https://github.com/poanetwork/blockscout/pull/2216) - Improve token's controllers by avoiding unnecessary preloads
- [#2235](https://github.com/poanetwork/blockscout/pull/2235) - save and show additional validation fields to smart contract
- [#2190](https://github.com/poanetwork/blockscout/pull/2190) - show all token transfers
- [#2193](https://github.com/poanetwork/blockscout/pull/2193) - feat: add BLOCKSCOUT_HOST, and use it in API docs
- [#2266](https://github.com/poanetwork/blockscout/pull/2266) - allow excluding uncles from average block time calculation
### Fixes
- [#2286](https://github.com/poanetwork/blockscout/pull/2286) - banner stats issues on sm resolutions, transactions title issue
- [#2284](https://github.com/poanetwork/blockscout/pull/2284) - add 404 status for not existing pages
- [#2244](https://github.com/poanetwork/blockscout/pull/2244) - fix internal transactions failing to be indexed because of constraint
- [#2281](https://github.com/poanetwork/blockscout/pull/2281) - typo issues, dropdown issues
- [#2278](https://github.com/poanetwork/blockscout/pull/2278) - increase threshold for scientific notation
- [#2275](https://github.com/poanetwork/blockscout/pull/2275) - Description for networks selector
- [#2263](https://github.com/poanetwork/blockscout/pull/2263) - added an ability to close network selector on outside click
- [#2257](https://github.com/poanetwork/blockscout/pull/2257) - 'download csv' button added to different tabs
- [#2242](https://github.com/poanetwork/blockscout/pull/2242) - added styles for 'download csv' button
- [#2261](https://github.com/poanetwork/blockscout/pull/2261) - header logo aligned to the center properly
- [#2254](https://github.com/poanetwork/blockscout/pull/2254) - search length issue, tile link wrapping issue
- [#2238](https://github.com/poanetwork/blockscout/pull/2238) - header content alignment issue, hide navbar on outside click
- [#2229](https://github.com/poanetwork/blockscout/pull/2229) - gap issue between qr and copy button in token transfers, top cards width and height issue
- [#2201](https://github.com/poanetwork/blockscout/pull/2201) - footer columns fix
- [#2179](https://github.com/poanetwork/blockscout/pull/2179) - fix docker build error
- [#2165](https://github.com/poanetwork/blockscout/pull/2165) - sort blocks by timestamp when calculating average block time
@ -44,17 +73,29 @@
- [#2174](https://github.com/poanetwork/blockscout/pull/2174) - fix reward channel joining
- [#2186](https://github.com/poanetwork/blockscout/pull/2186) - fix net version test
- [#2196](https://github.com/poanetwork/blockscout/pull/2196) - Nethermind client fixes
- [#2167](https://github.com/poanetwork/blockscout/pull/2168) - feat: document eth rpc api mimicking endpoints
- [#2237](https://github.com/poanetwork/blockscout/pull/2237) - fix rsk total_supply
- [#2198](https://github.com/poanetwork/blockscout/pull/2198) - reduce transaction status and error constraint
- [#2167](https://github.com/poanetwork/blockscout/pull/2167) - feat: document eth rpc api mimicking endpoints
- [#2225](https://github.com/poanetwork/blockscout/pull/2225) - fix metadata decoding in Solidity 0.5.9 smart contract verification
- [#2204](https://github.com/poanetwork/blockscout/pull/2204) - fix large contract verification
- [#2258](https://github.com/poanetwork/blockscout/pull/2258) - reduce BlocksTransactionsMismatch memory footprint
- [#2247](https://github.com/poanetwork/blockscout/pull/2247) - hide logs search if there are no logs
- [#2248](https://github.com/poanetwork/blockscout/pull/2248) - sort block after query execution for average block time
- [#2249](https://github.com/poanetwork/blockscout/pull/2249) - More transaction controllers improvements
- [#2267](https://github.com/poanetwork/blockscout/pull/2267) - Modify implementation of `where_transaction_has_multiple_internal_transactions`
- [#2270](https://github.com/poanetwork/blockscout/pull/2270) - Remove duplicate params in `Indexer.Fetcher.TokenBalance`
- [#2268](https://github.com/poanetwork/blockscout/pull/2268) - remove not existing assigns in html code
- [#2276](https://github.com/poanetwork/blockscout/pull/2276) - remove port in docs
### Chore
- [#2127](https://github.com/poanetwork/blockscout/pull/2127) - use previouse chromedriver version
- [#2118](https://github.com/poanetwork/blockscout/pull/2118) - show only the last decompiled contract
### Chore
- [#2256](https://github.com/poanetwork/blockscout/pull/2256) - use the latest version of chromedriver
## 2.0.0-beta
### Features
- [#2044](https://github.com/poanetwork/blockscout/pull/2044) - New network selector.
- [#2091](https://github.com/poanetwork/blockscout/pull/2091) - Added "Question" modal.
- [#1963](https://github.com/poanetwork/blockscout/pull/1963), [#1959](https://github.com/poanetwork/blockscout/pull/1959), [#1948](https://github.com/poanetwork/blockscout/pull/1948), [#1936](https://github.com/poanetwork/blockscout/pull/1936), [#1925](https://github.com/poanetwork/blockscout/pull/1925), [#1922](https://github.com/poanetwork/blockscout/pull/1922), [#1903](https://github.com/poanetwork/blockscout/pull/1903), [#1874](https://github.com/poanetwork/blockscout/pull/1874), [#1895](https://github.com/poanetwork/blockscout/pull/1895), [#2031](https://github.com/poanetwork/blockscout/pull/2031), [#2073](https://github.com/poanetwork/blockscout/pull/2073), [#2074](https://github.com/poanetwork/blockscout/pull/2074), - added new themes and logos for poa, eth, rinkeby, goerli, ropsten, kovan, sokol, xdai, etc, rsk and default theme
- [#1726](https://github.com/poanetwork/blockscout/pull/2071) - Updated styles for the new smart contract page.
@ -80,6 +121,9 @@
- [#2100](https://github.com/poanetwork/blockscout/pull/2100) - feat: eth_get_balance rpc endpoint
### Fixes
- [#2228](https://github.com/poanetwork/blockscout/pull/2228) - favorites duplication issues, active radio issue
- [#2207](https://github.com/poanetwork/blockscout/pull/2207) - new 'download csv' button design
- [#2206](https://github.com/poanetwork/blockscout/pull/2206) - added styles for 'Download All Transactions as CSV' button
- [#2099](https://github.com/poanetwork/blockscout/pull/2099) - logs search input width
- [#2098](https://github.com/poanetwork/blockscout/pull/2098) - nav dropdown issue, logo size issue
- [#2082](https://github.com/poanetwork/blockscout/pull/2082) - dropdown styles, tooltip gap fix, 404 page added

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

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

@ -123,8 +123,8 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
@import "components/verify_other_explorers";
@import "components/errors";
@import "components/log-search";
@import "components/radio";
@import "components/network-selector";
@import "components/new_smart_contract";
@import "components/radio_big";
@import "components/btn_no_border";

@ -42,6 +42,12 @@ $card-tab-icon-color-active: #20b760 !default;
line-height: 1.2rem;
margin-bottom: 2rem;
&.lg-card-title {
@media (max-width: 374px) {
font-size: 13px;
}
}
&.margin-bottom-md {
margin-bottom: 25px;
}

@ -80,6 +80,12 @@ $dashboard-banner-chart-axis-font-color: $dashboard-stats-item-value-color !defa
flex-direction: row;
}
@media (max-width: 599px) {
padding-top: 0;
padding-bottom: 0;
flex-direction: column;
}
&::before {
border-radius: 2px;
content: "";
@ -107,6 +113,11 @@ $dashboard-banner-chart-axis-font-color: $dashboard-stats-item-value-color !defa
line-height: 1.2;
margin: 0 0 5px;
@media (max-width: 374px) {
position: relative;
top: -2px;
}
@include media-breakpoint-down(md) {
margin: 0 5px 0 0;
}
@ -140,13 +151,16 @@ $dashboard-banner-chart-axis-font-color: $dashboard-stats-item-value-color !defa
height: auto;
justify-content: flex-start;
margin-left: 0;
margin-top: 15px;
max-width: 100%;
padding: 20px 0 20px 20px;
width: 250px;
box-shadow: 0 0 35px 0 rgba(0, 0, 0, 0.2);
}
@include media-breakpoint-down(lg) {
margin-top: 15px;
}
@include media-breakpoint-down(sm) {
width: 100%;
}
@ -186,4 +200,17 @@ $dashboard-banner-chart-axis-font-color: $dashboard-stats-item-value-color !defa
}
@include stats-item($dashboard-stats-item-border-color, $dashboard-stats-item-label-color, $dashboard-stats-item-value-color);
.dashboard-banner-network-stats-item {
@media (max-width: 374px) {
padding-left: calc(0.6rem + 4px);
padding-right: 0.5rem;
}
}
.dashboard-banner-network-stats-value {
@media (max-width: 374px) {
font-size: 0.9rem;
}
}
}

@ -21,9 +21,9 @@ $navbar-logo-width: auto !default;
.nav-item {
font-size: 14px;
}
.navbar-nav {
flex-grow: 1;
.navbar-nav {
.nav-link {
align-items: center;
color: $header-links-color;
@ -151,7 +151,13 @@ $navbar-logo-width: auto !default;
}
@include media-breakpoint-up(xl) {
width: 280px;
width: 340px;
}
@media (min-width: 1366px) {
width: 500px;
}
@media (min-width: 1440px) {
width: 580px;
}
}
.input-group-append {
@ -198,7 +204,7 @@ $navbar-logo-width: auto !default;
width: 100%;
.awesomplete {
@include media-breakpoint-down(sm) {
@include media-breakpoint-down(lg) {
width: 100%;
}
}
@ -210,6 +216,11 @@ $navbar-logo-width: auto !default;
.navbar-brand {
margin-left: 0;
flex-shrink: 1;
display: inline-flex;
.navbar-logo {
max-width: 100%;
}
}
.navbar-logo {
@ -227,3 +238,18 @@ $navbar-logo-width: auto !default;
.add-border {
border: 1px solid transparentize($white, 0.30);
}
.navbar-collapse {
justify-content: flex-end;
align-items: flex-start;
flex-shrink: 0;
@media (min-width: 992px) {
align-items: center;
}
}
.navbar-container, .navbar-primary {
@include media-breakpoint-up(xl) {
padding-right: 0;
}
}

@ -0,0 +1,341 @@
$network-selector-overlay-background: $modal-overlay-color !default;
$network-selector-close-color: $primary !default;
$network-selector-horizontal-padding: 28px;
$network-selector-horizontal-mobile-padding: 14px;
$btn-network-selector-load-more-background: #fff !default;
$btn-network-selector-load-more-color: $primary !default;
$network-selector-search-input-color: #a3a9b5 !default;
$network-selector-tab-active-border-color: $primary !default;
$network-selector-item-icon-dimensions: 30px !default;
.network-selector-visible {
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: 0;
}
.network-selector-overlay {
background-color: rgba($network-selector-overlay-background, 0.9);
bottom: 0;
display: none;
left: 0;
position: fixed;
right: 0;
top: 0;
z-index: 123;
}
.network-selector-overlay-close {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.network-selector-wrapper {
display: flex;
height: 100%;
width: 100%;
}
.network-selector {
background-color: #fff;
display: flex;
flex-direction: column;
flex-grow: 1;
flex-shrink: 1;
margin-left: auto;
max-width: 398px;
min-width: 0;
padding-top: 28px;
position: relative;
transition: right 0.25s ease-out;
z-index: 2;
}
.network-selector-close {
flex-shrink: 0;
padding: 0 $network-selector-horizontal-padding;
margin: 0 0 8px;
svg {
cursor: pointer;
display: block;
margin-left: auto;
}
path {
fill: $network-selector-close-color;
}
}
.network-selector-text-container {
flex-shrink: 0;
margin: 0 0 15px;
padding: 0 $network-selector-horizontal-padding;
}
.network-selector-title {
color: #333;
font-size: 18px;
font-weight: normal;
line-height: 1.2;
margin: 0 0 10px;
padding: 0;
}
.network-selector-text {
color: #a3a9b5;
font-size: 12px;
font-weight: normal;
line-height: 1.67;
margin: 0;
padding: 0;
}
.network-selector-search-container {
align-items: center;
background-color: #f5f6fa;
display: flex;
flex-shrink: 0;
height: 62px;
margin: 0;
padding: 0 $network-selector-horizontal-padding;
path {
flex-grow: 0;
flex-shrink: 0;
fill: $network-selector-search-input-color;
}
}
.network-selector-search-input {
background-color: transparent;
border-color: transparent;
color: #333;
flex-grow: 1;
font-size: 14px;
font-weight: 600;
height: 100%;
outline: none;
padding: 0 20px 0 10px;
&[placeholder]{
color: $network-selector-search-input-color !important;
}
&::-webkit-input-placeholder { /* Chrome/Opera/Safari */
color: $network-selector-search-input-color !important;
}
&::-moz-placeholder { /* Firefox 19+ */
color: $network-selector-search-input-color !important;
}
&:-ms-input-placeholder { /* IE 10+ */
color: $network-selector-search-input-color !important;
}
&:-moz-placeholder { /* Firefox 18- */
color: $network-selector-search-input-color !important;
}
}
.network-selector-tabs-container {
border-bottom: 1px solid $base-border-color;
display: flex;
flex-shrink: 0;
margin: 0 $network-selector-horizontal-mobile-padding;
@media (min-width: 375px) {
margin: 0 $network-selector-horizontal-padding;
}
}
.network-selector-tab {
color: #a3a9b5;
cursor: pointer;
flex-shrink: 1;
font-size: 14px;
font-weight: 600;
line-height: 1.2;
min-width: 0;
padding: 20px 18px 15px;
position: relative;
text-align: center;
user-select: none;
white-space: nowrap;
&:hover {
color: #333;
}
&.active {
color: #333;
cursor: default;
&::after {
background-color: $network-selector-tab-active-border-color;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
bottom: 0;
content: "";
height: 4px;
left: 0;
position: absolute;
right: 0;
}
}
}
.network-selector-tab-content {
display: none;
&.active {
display: block;
}
}
.network-selector-item {
border-bottom: 1px solid $base-border-color;
display: flex;
position: relative;
.radio {
cursor: pointer;
margin: 0 15px 0 0;
input[type="radio"] {
cursor: pointer;
}
}
.radio-icon {
margin: 0;
}
&:last-child {
border-bottom: none;
}
}
.network-selector-item-url {
align-items: center;
cursor: pointer;
display: flex;
flex-grow: 1;
margin: 0;
padding: 20px 0;
&:hover {
.network-selector-item-type {
color: #333;
}
}
}
.network-selector-item-icon {
background-color: #dfdfdf;
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: contain;
border-radius: 50%;
flex-grow: 0;
flex-shrink: 0;
height: $network-selector-item-icon-dimensions;
margin: 0 15px 0 0;
width: $network-selector-item-icon-dimensions;
}
.network-selector-item-title {
color: #333;
flex-grow: 1;
font-size: 14px;
font-weight: normal;
line-height: 1.2;
overflow: hidden;
text-align: left;
text-overflow: ellipsis;
user-select: none;
white-space: nowrap;
}
.network-selector-item-type {
color: #a3a9b5;
flex-shrink: 0;
font-size: 14px;
font-weight: normal;
line-height: 1.2;
padding-left: 10px;
text-align: right;
user-select: none;
white-space: nowrap;
}
.network-selector-item-content {
align-items: center;
display: flex;
flex-grow: 1;
}
.network-selector-networks-container {
flex-grow: 1;
flex-shrink: 1;
min-height: 100px;
overflow: auto;
padding: 0 $network-selector-horizontal-mobile-padding;
@media (min-width: 375px) {
padding: 0 $network-selector-horizontal-padding;
}
}
.network-selector-load-more-container {
flex-shrink: 1;
padding: 0 $network-selector-horizontal-padding;
.btn-network-selector-load-more {
@include btn-line($btn-network-selector-load-more-background, $btn-network-selector-load-more-color);
width: 100%;
}
}
.network-selector-item-favorite {
align-items: center;
cursor: pointer;
display: flex;
flex-grow: 1;
flex-shrink: 0;
margin: 0;
max-width: 36px;
padding-left: 20px;
position: relative;
input[type="checkbox"] {
cursor: pointer;
height: 100%;
opacity: 0;
position: absolute;
width: 100%;
z-index: 5;
&:checked + svg {
position: relative;
z-index: 1;
path {
fill: #ffb20d;
}
}
}
&:hover {
path {
fill: rgba(#ffb20d, 0.4);
}
}
}
.network-selector-tab-content-empty {
font-size: 16px;
font-weight: 600;
padding: 40px;
text-align: center;
}

@ -0,0 +1,49 @@
$radio-color: $primary !default;
$radio-dimensions: 20px !default;
.radio {
align-items: center;
display: flex;
position: relative;
input[type="radio"] {
height: 100%;
opacity: 0;
position: absolute;
width: 100%;
z-index: 5;
&:checked + .radio-icon::before {
background-color: $radio-color;
border-radius: 50%;
content: "";
height: 12px;
left: 50%;
position: absolute;
top: 50%;
transform: translateX(-50%) translateY(-50%);
width: 12px;
}
}
.radio-icon {
border: 1px solid $base-border-color;
border-radius: 50%;
flex-grow: 0;
flex-shrink: 0;
height: $radio-dimensions;
margin: 0 10px 0 0;
position: relative;
width: $radio-dimensions;
z-index: 1;
}
.radio-text {
font-size: 14px;
font-weight: normal;
line-height: 1.2;
position: relative;
white-space: nowrap;
z-index: 1;
}
}

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

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

@ -1,6 +1,46 @@
.transaction-details-address {
font-size: 12px;
font-weight: bold;
line-height: 1.2;
margin: 0 0 12px;
.transaction-bottom-panel {
display: flex;
flex-direction: column;
@media (min-width: 768px) {
flex-direction: row;
justify-content: space-between;
align-items: flex-end;
}
}
.transaction-bottom-panel {
display: flex;
flex-direction: column;
@media (min-width: 768px) {
flex-direction: row;
justify-content: space-between;
align-items: flex-end;
}
}
.download-all-transactions {
text-align: center;
color: #a3a9b5;
font-size: 13px;
margin-top: 10px;
@media (min-width: 768px) {
margin-top: 30px;
}
.download-all-transactions-link {
display: inline-flex;
align-items: center;
text-decoration: none;
svg {
position: relative;
margin-left: 2px;
top: -3px;
left: 3px;
path {
fill: $primary;
}
}
&:hover {
text-decoration: underline;
}
}
}

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

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

@ -31,6 +31,9 @@ import './pages/chain'
import './pages/pending_transactions'
import './pages/transaction'
import './pages/transactions'
import './pages/favorites'
import './pages/network-search'
import './pages/layout'
import './pages/admin/tasks.js'
@ -56,3 +59,4 @@ import './lib/tooltip'
import './lib/modals'
import './lib/try_api'
import './lib/card_tabs'
import './lib/network_selector'

@ -219,6 +219,14 @@ export const elements = {
$el.hide()
}
},
'[csv-download]': {
render ($el, state) {
if (state.emptyResponse) {
return $el.hide()
}
return $el.show()
}
}
}

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

@ -0,0 +1,77 @@
import $ from 'jquery'
$(function () {
const mainBody = $('body')
const showNetworkSelector = $('.js-show-network-selector')
const hideNetworkSelector = $('.js-network-selector-close')
const hideNetworkSelectorOverlay = $('.js-network-selector-overlay-close')
const networkSelector = $('.js-network-selector')
const networkSelectorOverlay = $('.js-network-selector-overlay')
const networkSelectorTab = $('.js-network-selector-tab')
const networkSelectorTabContent = $('.js-network-selector-tab-content')
const networkSelectorItemURL = $('.js-network-selector-item-url')
const FADE_IN_DELAY = 250
showNetworkSelector.on('click', (e) => {
e.preventDefault()
openNetworkSelector()
})
hideNetworkSelector.on('click', (e) => {
e.preventDefault()
closeNetworkSelector()
})
hideNetworkSelectorOverlay.on('click', (e) => {
e.preventDefault()
closeNetworkSelector()
})
networkSelectorTab.on('click', function (e) {
e.preventDefault()
setNetworkTab($(this))
})
networkSelectorItemURL.on('click', function (e) {
window.location = $(this).attr('network-selector-item-url')
})
let setNetworkTab = (currentTab) => {
if (currentTab.hasClass('active')) return
networkSelectorTab.removeClass('active')
currentTab.addClass('active')
networkSelectorTabContent.removeClass('active')
$(`[network-selector-tab="${currentTab.attr('network-selector-tab-filter')}"]`).addClass('active')
}
let openNetworkSelector = () => {
mainBody.addClass('network-selector-visible')
networkSelectorOverlay.fadeIn(FADE_IN_DELAY)
setNetworkSelectorVisiblePosition()
}
let closeNetworkSelector = () => {
mainBody.removeClass('network-selector-visible')
networkSelectorOverlay.fadeOut(FADE_IN_DELAY)
setNetworkSelectorHiddenPosition()
}
let getNetworkSelectorWidth = () => {
return parseInt(networkSelector.css('width')) || parseInt(networkSelector.css('max-width'))
}
let setNetworkSelectorHiddenPosition = () => {
return networkSelector.css({ 'right': `-${getNetworkSelectorWidth()}px` })
}
let setNetworkSelectorVisiblePosition = () => {
return networkSelector.css({ 'right': '0' })
}
let init = () => {
setNetworkSelectorHiddenPosition()
}
init()
})

@ -40,6 +40,15 @@ const elements = {
return $el.hide()
}
return $el.show()
}
},
'[data-search]': {
render ($el, state) {
if (state.emptyResponse) {
return $el.hide()
}
return $el.show()
}
}

@ -142,7 +142,7 @@ const elements = {
chart = createMarketHistoryChart($el[0])
},
render ($el, state, oldState) {
if (!chart || (oldState.availableSupply === state.availableSupply && oldState.marketHistoryData === state.marketHistoryData)) return
if (!chart || (oldState.availableSupply === state.availableSupply && oldState.marketHistoryData === state.marketHistoryData) || !state.availableSupply) return
chart.update(state.availableSupply, state.marketHistoryData)
}
},

@ -0,0 +1,58 @@
import $ from 'jquery'
var favoritesContainer = $('.js-favorites-tab')
var favoritesNetworksUrls = []
if (localStorage.getItem('favoritesNetworksUrls') === null) {
localStorage.setItem('favoritesNetworksUrls', JSON.stringify(favoritesNetworksUrls))
} else {
favoritesNetworksUrls = JSON.parse(localStorage.getItem('favoritesNetworksUrls'))
}
$(document).on('change', ".network-selector-item-favorite input[type='checkbox']", function () {
var networkUrl = $(this).attr('data-url')
var thisStatus = $(this).is(':checked')
var workWith = $(".network-selector-item[data-url='" + networkUrl + "'")
// Add new checkbox status to same network in another tabs
$(".network-selector-item-favorite input[data-url='" + networkUrl + "']").prop('checked', thisStatus)
// Clone
var parent = $(".network-selector-item[data-url='" + networkUrl + "'").clone()
// Push or remove favorite networks to array
var found = $.inArray(networkUrl, favoritesNetworksUrls)
if (found < 0 && thisStatus === true) {
favoritesNetworksUrls.push(networkUrl)
} else {
var index = favoritesNetworksUrls.indexOf(networkUrl)
if (index !== -1) {
favoritesNetworksUrls.splice(index, 1)
}
}
// Push to localstorage
var willBePushed = JSON.stringify(favoritesNetworksUrls)
localStorage.setItem('favoritesNetworksUrls', willBePushed)
// Append or remove item from 'favorites' tab
if (thisStatus === true) {
favoritesContainer.append(parent[0])
$('.js-favorites-tab .network-selector-tab-content-empty').hide()
} else {
var willRemoved = favoritesContainer.find(workWith)
willRemoved.remove()
if (favoritesNetworksUrls.length === 0) {
$('.js-favorites-tab .network-selector-tab-content-empty').show()
}
}
})
if (favoritesNetworksUrls.length > 0) {
$('.js-favorites-tab .network-selector-tab-content-empty').hide()
for (var i = 0; i < favoritesNetworksUrls.length + 1; i++) {
$(".network-selector-item[data-url='" + favoritesNetworksUrls[i] + "'").find('input[data-url]').prop('checked', true)
var parent = $(".network-selector-item[data-url='" + favoritesNetworksUrls[i] + "'").clone()
favoritesContainer.append(parent[0])
}
}

@ -0,0 +1,9 @@
import $ from 'jquery'
$(document).click(function (event) {
var clickover = $(event.target)
var _opened = $('.navbar-collapse').hasClass('show')
if (_opened === true && $('.navbar').find(clickover).length < 1) {
$('.navbar-toggler').click()
}
})

@ -0,0 +1,21 @@
import $ from 'jquery'
var networkSearchInput = $('.network-selector-search-input')
var networkSearchInputVal = ''
$(networkSearchInput).on('input', function () {
networkSearchInputVal = $(this).val()
$.expr[':'].Contains = $.expr.createPseudo(function (arg) {
return function (elem) {
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0
}
})
if (networkSearchInputVal === '') {
$('.network-selector-item').show()
} else {
$('.network-selector-item').hide()
$(".network-selector-item:Contains('" + networkSearchInputVal + "')").show()
}
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 60 60">
<g>
<path d="M.87,4h0V26.18a1.35,1.35,0,0,0,1.35,1.37l9.91,0a1.37,1.37,0,0,0,1.16-2L12,23.19h0a2,2,0,0,0-2.75-.75h0l-.14.08h0a2,2,0,0,1-2.72-.74,2.06,2.06,0,0,1-.16-1.65h0l2.06-6.28h0a2,2,0,0,1,2.31-1.36h0L17,13.8h0a2,2,0,0,1,1.33,1,2,2,0,0,1-.73,2.77h0l-.14.08h0a2.07,2.07,0,0,0-.74,2.8h0L18,22.65a1.34,1.34,0,0,0,2.32,0l5-8.66a1.37,1.37,0,0,0-.49-1.86L5.9,1.06h0A3.36,3.36,0,0,0,.87,4Z"/>
<path d="M50.9,26.32h0L30.58,14.37a1.34,1.34,0,0,0-1.85.5L23.4,24.36a1.47,1.47,0,0,0,1.25,2.19h3.48a1.45,1.45,0,0,0,1.44-1.47V24A1.34,1.34,0,0,1,30.9,22.6h.92a1.33,1.33,0,0,1,1,.46l4.84,5.56a1.38,1.38,0,0,1,0,1.81L32.83,36a1.33,1.33,0,0,1-1,.46H30.9a1.34,1.34,0,0,1-1.33-1.35V34a1.45,1.45,0,0,0-1.44-1.47H24.7a1.47,1.47,0,0,0-1.25,2.19l5.29,9.43a1.33,1.33,0,0,0,1.83.49L50.9,32.67h0A3.7,3.7,0,0,0,50.9,26.32Z"/>
<path d="M5.9,58.94h0L24.76,47.87a1.39,1.39,0,0,0,.5-1.88l-5-8.71a1.34,1.34,0,0,0-2.32,0l-1.62,2.86a1.38,1.38,0,0,0,.5,1.87l1.27.75a1.38,1.38,0,0,1,.5,1.86l-.48.84a1.34,1.34,0,0,1-.89.65L10,47.61a1.34,1.34,0,0,1-1.54-.91L6.08,39.56a1.39,1.39,0,0,1,.11-1.12l.47-.83a1.34,1.34,0,0,1,1.84-.49l1.27.75a1.33,1.33,0,0,0,1.83-.49l1.61-2.83a1.37,1.37,0,0,0-1.16-2l-9.84,0A1.35,1.35,0,0,0,.87,33.82V56h0A3.36,3.36,0,0,0,5.9,58.94Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">
<path fill="#5C34A2" fill-rule="evenodd" d="M15 0c8.284 0 15 6.716 15 15 0 8.284-6.716 15-15 15-8.284 0-15-6.716-15-15C0 6.716 6.716 0 15 0z"/>
<path fill="#FFF" fill-rule="evenodd" d="M8.438 16.812h-.313v.875h-.007c.001.007.007.01.007.016 0 .164-.14.297-.313.297h-2.5A.305.305 0 0 1 5 17.703c0-.006.006-.009.007-.016H5v-5.374h.007c-.001-.007-.007-.01-.007-.016 0-.164.14-.297.312-.297h3.126c1.38 0 2.5 1.077 2.5 2.406 0 1.329-1.12 2.406-2.5 2.406zM14.688 12c1.725 0 3.125 1.343 3.125 3s-1.4 3-3.125 3c-1.726 0-3.126-1.343-3.126-3s1.4-3 3.126-3zM25 17.81a.19.19 0 0 1-.193.185h-7.108c-.004.001-.007.005-.011.005a.188.188 0 0 1-.188-.188c0-.039.021-.07.042-.101l-.007-.012 3.52-5.549h.016c.02-.082.086-.148.177-.148.092 0 .158.066.178.148h.016L25 17.736l-.02.027c.005.017.02.029.02.047z"/>
</svg>

After

Width:  |  Height:  |  Size: 868 B

@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="25" height="25" viewBox="0 0 43.5 43.5">
<defs>
<style>
.a {
fill: #fff;
filter: url(#b);
}
.b {
fill: url(#a);
}
</style>
<linearGradient id="a" x1="21" y1="12.89" x2="21" y2="28.58" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fec54c"/>
<stop offset="1" stop-color="#fe9314"/>
</linearGradient>
<filter filterUnits="userSpaceOnUse" height="49" id="b" width="43" x="0" y="0">
<feOffset dy="3" in="SourceAlpha"></feOffset>
<feGaussianBlur result="blurOut" stdDeviation="2.236"></feGaussianBlur>
<feFlood flood-color="#ABBAC7" result="floodOut"></feFlood>
<feComposite in="floodOut" in2="blurOut" operator="atop"></feComposite>
<feComponentTransfer>
<feFuncA slope=".2" type="linear"></feFuncA>
</feComponentTransfer>
<feMerge>
<feMergeNode></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
<path class="a" d="M17.2,38.8,6,32.1a4.34,4.34,0,0,1-2-3.4V12.9A4.11,4.11,0,0,1,6,9.5L17.2,2.8C21,.5,21,.5,24.9,2.8L36,9.4a4.22,4.22,0,0,1,2,3.4V28.6A4.22,4.22,0,0,1,36,32L24.9,38.7C21,41.1,21,41.1,17.2,38.8Z"/>
<polygon class="b" points="30.03 12.89 24.97 12.89 21.03 17.64 17.03 12.89 11.97 12.89 18.53 20.82 11.97 28.7 17.03 28.7 20.97 23.95 24.97 28.7 30.03 28.7 23.47 20.76 30.03 12.89"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">
<path fill="#38A9F5" fill-rule="evenodd" d="M15 0c8.284 0 15 6.716 15 15 0 8.284-6.716 15-15 15-8.284 0-15-6.716-15-15C0 6.716 6.716 0 15 0z"/>
<path fill="#FFF" fill-rule="evenodd" d="M24.22 20h-6.449c-.089 0-.178-.034-.266-.06l.013.06h-2.781l-.836-3.687H5.437v-.078c-.245-.038-.437-.277-.437-.579v-4.469h.003c.007-.65.448-1.173.997-1.183V10h15.95c.558 0 1.133.535 1.282 1.194l1.729 7.612c.149.659-.182 1.194-.741 1.194zM9.054 11.84H6.776l.61 2.638h2.279l-.611-2.638zm3.78 0h-2.279l.611 2.638h2.278l-.61-2.638zm6.226 6.841h1.05l-.282-1.308h-1.05l.282 1.308zm2.761-6.841h-4.375l1.175 5.001h4.376l-1.176-5.001zm1.343 5.533h-1.05l.282 1.308h1.05l-.282-1.308zm-17.727.002H11.664v-.003h1.452L13.668 20H6.125v-.026c-.038.005-.071.026-.109.026-.51 0-.914-.452-.987-1.031H5v-.813h.082c-.038-.077-.082-.153-.082-.25 0-.293.196-.531.437-.531z"/>
</svg>

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">
<path fill="#6EC9B9" fill-rule="evenodd" d="M15 0c8.284 0 15 6.716 15 15 0 8.284-6.716 15-15 15-8.284 0-15-6.716-15-15C0 6.716 6.716 0 15 0z"/>
<path fill="#FFF" fill-rule="evenodd" d="M24.22 20h-6.449c-.089 0-.178-.034-.266-.06l.013.06h-2.781l-.836-3.687H5.437v-.078c-.245-.038-.437-.277-.437-.579v-4.469h.003c.007-.65.448-1.173.997-1.183V10h15.95c.558 0 1.133.535 1.282 1.194l1.729 7.612c.149.659-.182 1.194-.741 1.194zM9.054 11.84H6.776l.61 2.637h2.279l-.611-2.637zm3.78 0h-2.279l.611 2.637h2.278l-.61-2.637zm6.226 6.841h1.05l-.282-1.308h-1.05l.282 1.308zm2.761-6.841h-4.375l1.175 5.001h4.376l-1.176-5.001zm1.343 5.533h-1.05l.282 1.308h1.05l-.282-1.308zm-17.727.002H11.664v-.003h1.452L13.668 20H6.125v-.026c-.038.005-.071.026-.109.026-.51 0-.914-.452-.987-1.031H5v-.813h.082c-.038-.077-.082-.153-.082-.25 0-.293.196-.531.437-.531z"/>
</svg>

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -41,7 +41,7 @@ config :block_scout_web, BlockScoutWeb.Endpoint,
]
],
url: [
host: "localhost",
host: System.get_env("BLOCKSCOUT_HOST") || "localhost",
path: System.get_env("NETWORK_PATH") || "/"
],
render_errors: [view: BlockScoutWeb.ErrorView, accepts: ~w(html json)],

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

@ -10,6 +10,7 @@ defmodule BlockScoutWeb.AddressTransactionController do
alias BlockScoutWeb.TransactionView
alias Explorer.{Chain, Market}
alias Explorer.Chain.{AddressTokenTransferCsvExporter, AddressTransactionCsvExporter}
alias Explorer.ExchangeRates.Token
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
@ -106,4 +107,44 @@ defmodule BlockScoutWeb.AddressTransactionController do
not_found(conn)
end
end
def token_transfers_csv(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
address
|> AddressTokenTransferCsvExporter.export()
|> Enum.into(
conn
|> put_resp_content_type("application/csv")
|> put_resp_header("content-disposition", "attachment; filename=token_transfers.csv")
|> send_chunked(200)
)
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
def transactions_csv(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
address
|> AddressTransactionCsvExporter.export()
|> Enum.into(
conn
|> put_resp_content_type("application/csv")
|> put_resp_header("content-disposition", "attachment; filename=transactions.csv")
|> send_chunked(200)
)
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
end

@ -17,7 +17,7 @@ defmodule BlockScoutWeb.BlockTransactionController do
Keyword.merge(
[
necessity_by_association: %{
:block => :required,
:block => :optional,
[created_contract_address: :names] => :optional,
[from_address: :names] => :required,
[to_address: :names] => :optional

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

@ -12,7 +12,7 @@ defmodule BlockScoutWeb.RecentTransactionsController do
necessity_by_association: %{
:block => :required,
[created_contract_address: :names] => :optional,
[from_address: :names] => :required,
[from_address: :names] => :optional,
[to_address: :names] => :optional
},
paging_options: %PagingOptions{page_size: 5}

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

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

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

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

@ -61,21 +61,16 @@ defmodule BlockScoutWeb.TransactionController do
end
def show(conn, %{"id" => id}) do
case Chain.string_to_transaction_hash(id) do
{:ok, transaction_hash} -> show_transaction(conn, id, Chain.hash_to_transaction(transaction_hash))
:error -> conn |> put_status(422) |> render("invalid.html", transaction_hash: id)
end
end
defp show_transaction(conn, id, {:error, :not_found}) do
conn |> put_status(404) |> render("not_found.html", transaction_hash: id)
end
defp show_transaction(conn, id, {:ok, %Chain.Transaction{} = transaction}) do
if Chain.transaction_has_token_transfers?(transaction.hash) do
redirect(conn, to: transaction_token_transfer_path(conn, :index, id))
with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(id),
{:ok, %Chain.Transaction{} = transaction} <- Chain.hash_to_transaction(transaction_hash) do
if Chain.transaction_has_token_transfers?(transaction.hash) do
redirect(conn, to: transaction_token_transfer_path(conn, :index, id))
else
redirect(conn, to: transaction_internal_transaction_path(conn, :index, id))
end
else
redirect(conn, to: transaction_internal_transaction_path(conn, :index, id))
:error -> conn |> put_status(422) |> render("invalid.html", transaction_hash: id)
{:error, :not_found} -> conn |> put_status(404) |> render("not_found.html", transaction_hash: id)
end
end
end

@ -17,7 +17,8 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
necessity_by_association: %{
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional
[to_address: :names] => :optional,
[transaction: :block] => :optional
}
],
paging_options(params)

@ -19,15 +19,7 @@ defmodule BlockScoutWeb.TransactionRawTraceController do
:token_transfers => :optional
}
) do
options = [
necessity_by_association: %{
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional
}
]
internal_transactions = Chain.transaction_to_internal_transactions(transaction, options)
internal_transactions = Chain.transaction_to_internal_transactions(transaction)
render(
conn,

@ -242,8 +242,12 @@ defmodule BlockScoutWeb.Router do
get("/search_logs", AddressLogsController, :search_logs)
get("/transactions_csv", AddressTransactionController, :transactions_csv)
get("/token_autocomplete", ChainController, :token_autocomplete)
get("/token_transfers_csv", AddressTransactionController, :token_transfers_csv)
get("/chain_blocks", ChainController, :chain_blocks, as: :chain_blocks)
get("/api_docs", APIDocsController, :index)

@ -5,8 +5,8 @@
<%= if assigns[:truncate] do %>
<%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %>
<% else %>
<span class="d-none d-md-none d-lg-inline"><%= @address %></span>
<span class="d-md-inline-block d-lg-none"><%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %></span>
<span class="d-none d-md-none d-xl-inline"><%= @address %></span>
<span class="d-md-inline-block d-xl-none"><%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %></span>
<% end %>
<% end %>
</span>

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

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

@ -7,8 +7,8 @@
<div class="card-body" data-async-load data-async-listing="<%= @current_path %>">
<h2 class="card-title"><%= gettext "Logs" %></h2>
<div class="logs-topbar">
<div class="logs-search">
<div data-search class="logs-search">
<input data-search-field class="logs-search-input" type='text' placeholder=<%= gettext("Topic") %> id='search-text-input' />
<button data-cancel-search-button class="logs-search-btn-cancel" type="button">x</button>
<button data-search-button class="logs-search-btn" type="button"><%= gettext("Search") %></button>

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

@ -18,8 +18,10 @@
<div class="dropdown-menu dropdown-menu-right token-balance-dropdown p-0" aria-labelledby="dropdown-tokens">
<div data-dropdown-items class="dropdown-items">
<div class="m-3 position-relative">
<i class="fas fa-search position-absolute dropdown-search-icon"></i>
<div class="position-relative">
<svg class="position-absolute dropdown-search-icon" viewBox="0 0 16 17" xmlns="http://www.w3.org/2000/svg" width="16" height="17" class="dropdown-search-icon position-absolute">
<path fill="#7DD79F" fill-rule="evenodd" d="M15.713 15.727a.982.982 0 0 1-1.388 0l-2.289-2.29C10.773 14.403 9.213 15 7.5 15A7.5 7.5 0 1 1 15 7.5c0 1.719-.602 3.284-1.575 4.55l2.288 2.288a.983.983 0 0 1 0 1.389zM7.5 2a5.5 5.5 0 1 0 0 11 5.5 5.5 0 1 0 0-11z"></path>
</svg>
<%= text_input(
:token_search,
:name,

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

@ -2,9 +2,9 @@
<div class="card">
<div class="card-body">
<h1 class="card-title margin-bottom-sm"><%= gettext("ETH RPC API Documentation") %></h2>
<p class="api-text-monospace" data-endpoint-url="<%= BlockScoutWeb.Endpoint.url() %>/api/eth_rpc">[ <%= gettext "Base URL:" %> <%= @conn.host %>/api/eth_rpc ]</p>
<p class="api-text-monospace" data-endpoint-url="<%= blockscout_url() %>/api/eth_rpc">[ <%= gettext "Base URL:" %> <%= blockscout_url() %>/api/eth_rpc ]</p>
<p class="card-subtitle margin-bottom-0">
<%= gettext "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found " %>
<%= gettext "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found " %>
<a href="https://github.com/ethereum/wiki/wiki/JSON-RPC"><%= gettext "here." %></a>
<%= gettext "This is useful to allow sending requests to blockscout without having to change anything about the request." %>

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

@ -105,7 +105,7 @@
<div class="card card-chain-transactions">
<div class="card-body">
<%= link(gettext("View All Transactions"), to: transaction_path(BlockScoutWeb.Endpoint, :index), class: "btn-line float-right") %>
<h2 class="card-title"><%= gettext "Transactions" %></h2>
<h2 class="card-title lg-card-title"><%= gettext "Transactions" %></h2>
<div data-selector="channel-batching-message" style="display: none;">
<div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a>

@ -0,0 +1,53 @@
<div class="network-selector-overlay js-network-selector-overlay">
<div class="network-selector-overlay-close js-network-selector-overlay-close"></div>
<div class="network-selector-wrapper">
<div class="network-selector js-network-selector">
<div class="network-selector-close js-network-selector-close">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13">
<path fill-rule="evenodd" d="M7.881 6.5l4.834 4.834a.977.977 0 0 1-1.381 1.381L6.5 7.881l-4.834 4.834a.977.977 0 0 1-1.381-1.381L5.119 6.5.285 1.666A.977.977 0 0 1 1.666.285L6.5 5.119 11.334.285a.977.977 0 0 1 1.381 1.381L7.881 6.5z"/>
</svg>
</div>
<div class="network-selector-text-container">
<h1 class="network-selector-title"><%= gettext("Change Network") %></h1>
<p class="network-selector-text"><%= gettext("Use the search box to find a hosted network, or select from the list of available networks below.") %></p>
</div>
<form class="network-selector-search-container">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="17">
<path fill-rule="evenodd" d="M15.713 15.727a.982.982 0 0 1-1.388 0l-2.289-2.29C10.773 14.403 9.213 15 7.5 15A7.5 7.5 0 1 1 15 7.5c0 1.719-.602 3.285-1.575 4.55l2.288 2.288a.983.983 0 0 1 0 1.389zM7.5 2a5.5 5.5 0 1 0 0 11 5.5 5.5 0 1 0 0-11z"/>
</svg>
<input class="network-selector-search-input" type="text" placeholder='<%= gettext("Search network") %>' />
</form>
<div class="network-selector-tabs-container">
<div class="network-selector-tab js-network-selector-tab active" network-selector-tab-filter="all"><%= gettext("All") %></div>
<div class="network-selector-tab js-network-selector-tab" network-selector-tab-filter="mainnet"><%= gettext("Mainnet") %></div>
<div class="network-selector-tab js-network-selector-tab" network-selector-tab-filter="testnet"><%= gettext("Testnet") %></div>
<div class="network-selector-tab js-network-selector-tab" network-selector-tab-filter="favorites"><%= gettext("Favorites") %></div>
</div>
<div class="network-selector-networks-container">
<% main_nets = dropdown_main_nets() %>
<% test_nets = dropdown_test_nets() %>
<div class="network-selector-tab-content js-network-selector-tab-content active" network-selector-tab="all">
<%= for %{url: url, title: title} <- main_nets do %>
<%= render BlockScoutWeb.LayoutView, "_network_selector_item.html", title: title, url: url, tab_type: "Mainnet" %>
<% end %>
<%= for %{url: url, title: title} <- test_nets do %>
<%= render BlockScoutWeb.LayoutView, "_network_selector_item.html", title: title, url: url, tab_type: "Testnet" %>
<% end %>
</div>
<div class="network-selector-tab-content js-network-selector-tab-content" network-selector-tab="mainnet">
<%= for %{url: url, title: title} <- main_nets do %>
<%= render BlockScoutWeb.LayoutView, "_network_selector_item.html", title: title, url: url, tab_type: "Mainnet" %>
<% end %>
</div>
<div class="network-selector-tab-content js-network-selector-tab-content" network-selector-tab="testnet">
<%= for %{url: url, title: title} <- test_nets do %>
<%= render BlockScoutWeb.LayoutView, "_network_selector_item.html", title: title, url: url, tab_type: "Testnet" %>
<% end %>
</div>
<div class="network-selector-tab-content js-network-selector-tab-content js-favorites-tab" network-selector-tab="favorites">
<div class="network-selector-tab-content-empty">No content.</div>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,25 @@
<div class="network-selector-item" data-url="<%= @url %>" data-name="<%= @title %>">
<label class="network-selector-item-url js-network-selector-item-url" network-selector-item-url="<%= @url %>">
<span class="radio">
<input type="radio" name="networkSelectorItem" <%= if @title == subnetwork_title() do %> checked="true" <% end %> />
<span class="radio-icon"></span>
</span>
<span class="network-selector-item-content">
<span class='network-selector-item-icon network-selector-item-icon-<%= String.downcase(String.replace(@title, " ", "-")) %>' style="background-image: url('/images/network-selector-icons/<%= String.downcase(String.replace(@title, " ", "-")) %>.png');"></span>
<span class="network-selector-item-title">
<%= @title %>
</span>
<%= if @tab_type do %>
<span class="network-selector-item-type">
<%= @tab_type %>
</span>
<% end %>
</span>
</label>
<label class="network-selector-item-favorite">
<input type="checkbox" data-url="<%= @url %>" />
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="15">
<path fill="#E2E5EC" fill-rule="evenodd" d="M15.647 6.795c.315-.3.426-.741.29-1.151a1.135 1.135 0 0 0-.926-.764l-3.871-.551a.501.501 0 0 1-.381-.271L9.028.624A1.143 1.143 0 0 0 8-.001c-.44 0-.834.24-1.028.625L5.24 4.059a.506.506 0 0 1-.381.271l-3.871.55c-.435.062-.79.355-.926.765-.136.409-.025.85.29 1.15l2.801 2.673a.492.492 0 0 1 .146.439l-.661 3.774c-.058.333.031.656.25.911.342.397.937.518 1.414.272l3.462-1.782a.53.53 0 0 1 .471 0l3.463 1.782a1.16 1.16 0 0 0 1.413-.272c.22-.255.309-.579.25-.911L12.7 9.907a.489.489 0 0 1 .146-.439l2.801-2.673z"/>
</svg>
</label>
</div>

@ -1,5 +1,5 @@
<nav class="navbar navbar-dark navbar-expand-lg navbar-primary" data-selector="navbar">
<div class="container">
<div class="container-fluid navbar-container">
<%= link to: chain_path(@conn, :show), class: "navbar-brand", "data-test": "header_logo" do %>
<img class="navbar-logo" src="<%= logo() %>" alt="<%= subnetwork_title() %>" />
<% end %>
@ -7,7 +7,7 @@
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link topnav-nav-link dropdown-toggle" href="#" id="navbarBlocksDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
@ -82,26 +82,12 @@
</div>
</li>
<li class="nav-item dropdown nav-item-networks">
<a class="nav-link topnav-nav-link <%= if dropdown_nets() == [], do: "disabled", else: "dropdown-toggle" %>" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<a class="nav-link topnav-nav-link dropdown-toggle active-icon js-show-network-selector <%= if dropdown_nets() == [], do: "disabled" %>" 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>
<%= subnetwork_title() %>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item header division">Mainnets</a>
<%= for %{url: url, title: title} <- dropdown_head_main_nets() do %>
<a class="dropdown-item" href="<%= url%>"><%= title %></a>
<% end %>
<a class="dropdown-item header division">Testnets</a>
<%= for %{url: url, title: title} <- test_nets(dropdown_nets()) do %>
<a class="dropdown-item" href="<%= url%>"><%= title %></a>
<% end %>
<a class="dropdown-item header division">Other Networks</a>
<%= for %{url: url, title: title} <- dropdown_other_nets() do %>
<a class="dropdown-item" href="<%= url%>"><%= title %></a>
<% end %>
</div>
</li>
</ul>
<!-- Search navbar -->
@ -136,3 +122,4 @@
</div>
</div>
</nav>
<%= render BlockScoutWeb.LayoutView, "_network_selector.html" %>

@ -1,6 +1,6 @@
<section>
<section class="address-overview">
<div class="row">
<div class="col-md-12 col-lg-8">
<div class="card-section col-md-12 col-lg-8 pr-0-md">
<div class="card">
<div class="card-body">
<h1 class="card-title">
@ -11,7 +11,7 @@
<% end %>
<!-- buttons -->
<span class="overview-title-buttons float-right">
<span data-clipboard-text="<%= @token.contract_address_hash %>">
<span class="overview-title-item" data-clipboard-text="<%= @token.contract_address_hash %>">
<span
aria-label='<%= gettext("Copy Address") %>'
class="btn-copy-icon"
@ -25,6 +25,7 @@
</span>
</span>
<span
class="overview-title-item"
data-target="#qrModal"
data-toggle="modal"
>
@ -68,7 +69,7 @@
</div>
<%= if total_supply?(@token) do %>
<div class="col-md-6 col-lg-4">
<div class="card-section col-md-12 col-lg-4 pl-0-md">
<div class="card card-background-1">
<div class="card-body">
<h2 class="card-title balance-card-title"><%= gettext "Total Supply" %></h2>

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

@ -170,17 +170,21 @@
</div>
<%= case token_transfer_type(@transaction) do %>
<% {type, token_transfer} -> %>
<% {type, transaction_with_transfers} when is_atom(type) -> %>
<div class="col-md-12 col-lg-4 d-flex flex-column flex-md-row flex-lg-column pl-0">
<!-- Value -->
<div class="card card-background-1 flex-grow-1">
<div class="card-body card-body-flex-column-space-between">
<h2 class="card-title balance-card-title"><%= if type == :erc20, do: gettext("ERC-20"), else: gettext("ERC-721")%><%= gettext " Token Transfer" %></h2>
<h2 class="card-title balance-card-title"><%= token_type_name(type)%><%= gettext " Token Transfer" %></h2>
<div class="text-right">
<%= for transfer <- transaction_with_transfers.token_transfers do %>
<h3 class="address-balance-text">
<%= token_transfer_amount(token_transfer) %>
<%= link(token_symbol(token_transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, token_transfer.token.contract_address_hash)) %>
<%= token_transfer_amount(transfer) %>
<%= link(token_symbol(transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, transfer.token.contract_address_hash)) %>
</h3>
<% end %>
</div>
</div>
</div>

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.APIDocsView do
use BlockScoutWeb, :view
alias BlockScoutWeb.LayoutView
alias BlockScoutWeb.{Endpoint, LayoutView}
def action_tile_id(module, action) do
"#{module}-#{action}"
@ -33,4 +33,12 @@ defmodule BlockScoutWeb.APIDocsView do
"&#{param.key}=" <> "{<strong>#{param.placeholder}</strong>}"
end)
end
def blockscout_url do
if System.get_env("BLOCKSCOUT_HOST") do
"http://" <> System.get_env("BLOCKSCOUT_HOST")
else
Endpoint.url()
end
end
end

@ -198,6 +198,16 @@ defmodule BlockScoutWeb.LayoutView do
|> Enum.reject(&Map.get(&1, :hide_in_dropdown?))
end
def dropdown_main_nets do
dropdown_nets()
|> main_nets()
end
def dropdown_test_nets do
dropdown_nets()
|> test_nets()
end
def dropdown_head_main_nets do
dropdown_nets()
|> main_nets()

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.TransactionView do
alias BlockScoutWeb.{AddressView, BlockView, TabHelpers}
alias Cldr.Number
alias Explorer.Chain
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Block.Reward
alias Explorer.Chain.{Address, Block, InternalTransaction, Transaction, Wei}
alias Explorer.ExchangeRates.Token
@ -33,7 +33,18 @@ defmodule BlockScoutWeb.TransactionView do
def value_transfer?(_), do: false
def token_transfer_type(transaction) do
Chain.transaction_token_transfer_type(transaction)
transaction_with_transfers = Repo.preload(transaction, token_transfers: :token)
type = Chain.transaction_token_transfer_type(transaction)
if type, do: {type, transaction_with_transfers}
end
def token_type_name(type) do
case type do
:erc20 -> gettext("ERC-20 ")
:erc721 -> gettext("ERC-721 ")
:token_transfer -> ""
end
end
def processing_time_duration(%Transaction{block: nil}) do

@ -49,7 +49,7 @@ msgid "%{subnetwork} Explorer - BlockScout"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:133
#: lib/block_scout_web/views/transaction_view.ex:144
msgid "(Awaiting internal transactions for status)"
msgstr ""
@ -105,13 +105,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:4
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:59
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:60
msgid "Addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:23
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:21
#: lib/block_scout_web/views/address_internal_transaction_view.ex:8
#: lib/block_scout_web/views/address_transaction_view.ex:8
msgid "All"
@ -210,8 +211,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37
#: lib/block_scout_web/templates/address/overview.html.eex:144
#: lib/block_scout_web/templates/address/overview.html.eex:152
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:106
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:114
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:107
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:115
msgid "Close"
msgstr ""
@ -257,7 +258,7 @@ msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:53
#: lib/block_scout_web/templates/address_contract/index.html.eex:82
msgid "Contract ABI"
msgstr ""
@ -275,12 +276,12 @@ msgid "Contract Address Pending"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:210
#: lib/block_scout_web/views/transaction_view.ex:221
msgid "Contract Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:209
#: lib/block_scout_web/views/transaction_view.ex:220
msgid "Contract Creation"
msgstr ""
@ -295,7 +296,7 @@ msgid "Contract name:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:41
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
msgid "Contract source code"
msgstr ""
@ -363,12 +364,12 @@ msgid "Error trying to fetch balances."
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:137
#: lib/block_scout_web/views/transaction_view.ex:148
msgid "Error: %{reason}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:135
#: lib/block_scout_web/views/transaction_view.ex:146
msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
@ -378,7 +379,7 @@ msgstr ""
#: lib/block_scout_web/templates/layout/app.html.eex:55
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/_tile.html.eex:30
#: lib/block_scout_web/templates/transaction/overview.html.eex:192
#: lib/block_scout_web/templates/transaction/overview.html.eex:196
#: lib/block_scout_web/views/wei_helpers.ex:78
msgid "Ether"
msgstr ""
@ -473,7 +474,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6
#: lib/block_scout_web/views/address_view.ex:306
#: lib/block_scout_web/views/transaction_view.ex:263
#: lib/block_scout_web/views/transaction_view.ex:274
msgid "Internal Transactions"
msgstr ""
@ -490,7 +491,7 @@ msgid "Less than"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:220
#: lib/block_scout_web/templates/transaction/overview.html.eex:224
msgid "Limit"
msgstr ""
@ -500,7 +501,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8
#: lib/block_scout_web/views/address_view.ex:312
#: lib/block_scout_web/views/transaction_view.ex:264
#: lib/block_scout_web/views/transaction_view.ex:275
msgid "Logs"
msgstr ""
@ -512,7 +513,7 @@ msgid "Market Cap"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:118
#: lib/block_scout_web/views/transaction_view.ex:129
msgid "Max of"
msgstr ""
@ -576,7 +577,7 @@ msgid "OUT"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_contract/index.html.eex:40
msgid "Optimization enabled"
msgstr ""
@ -602,8 +603,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44
#: lib/block_scout_web/views/transaction_view.ex:132
#: lib/block_scout_web/views/transaction_view.ex:166
#: lib/block_scout_web/views/transaction_view.ex:143
#: lib/block_scout_web/views/transaction_view.ex:177
msgid "Pending"
msgstr ""
@ -626,8 +627,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:33
#: lib/block_scout_web/templates/address/overview.html.eex:143
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:35
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:105
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:106
msgid "QR Code"
msgstr ""
@ -666,14 +667,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:116
#: lib/block_scout_web/templates/layout/_topnav.html.eex:133
#: lib/block_scout_web/templates/layout/_topnav.html.eex:102
#: lib/block_scout_web/templates/layout/_topnav.html.eex:119
msgid "Search"
msgstr ""
#, elixir-format
#:
#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:28
#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:30
msgid "Search tokens"
msgstr ""
@ -684,13 +685,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:34
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:37
msgid "Show QR Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8
#: lib/block_scout_web/views/transaction_view.ex:134
#: lib/block_scout_web/views/transaction_view.ex:145
msgid "Success"
msgstr ""
@ -743,7 +744,7 @@ msgid "There are no tokens."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:62
#: lib/block_scout_web/templates/address_transaction/index.html.eex:64
msgid "There are no transactions for this address."
msgstr ""
@ -795,7 +796,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:5
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:208
#: lib/block_scout_web/views/transaction_view.ex:219
msgid "Token Transfer"
msgstr ""
@ -805,7 +806,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7
#: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:262
#: lib/block_scout_web/views/transaction_view.ex:273
msgid "Token Transfers"
msgstr ""
@ -834,7 +835,7 @@ msgid "Total Difficulty"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:74
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:75
msgid "Total Supply"
msgstr ""
@ -845,7 +846,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:3
#: lib/block_scout_web/views/transaction_view.ex:211
#: lib/block_scout_web/views/transaction_view.ex:222
msgid "Transaction"
msgstr ""
@ -881,7 +882,7 @@ msgid "Transactions sent"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:60
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:61
msgid "Transfers"
msgstr ""
@ -907,7 +908,7 @@ msgid "Unique Token"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:214
#: lib/block_scout_web/templates/transaction/overview.html.eex:218
msgid "Used"
msgstr ""
@ -927,7 +928,7 @@ msgid "Validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:192
#: lib/block_scout_web/templates/transaction/overview.html.eex:196
msgid "Value"
msgstr ""
@ -943,7 +944,7 @@ msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:54
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:55
msgid "View Contract"
msgstr ""
@ -1048,7 +1049,7 @@ msgid "Self-Destruct"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:62
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:63
msgid "Decimals"
msgstr ""
@ -1210,7 +1211,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_logs/index.html.eex:21
#: lib/block_scout_web/templates/address_token/index.html.eex:13
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:20
#: lib/block_scout_web/templates/address_transaction/index.html.eex:57
#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_validation/index.html.eex:22
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/chain/show.html.eex:91
@ -1404,17 +1405,17 @@ msgid "Support"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:55
#: lib/block_scout_web/templates/address_contract/index.html.eex:84
msgid "Copy ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:71
#: lib/block_scout_web/templates/address_contract/index.html.eex:100
msgid "Copy Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:43
#: lib/block_scout_web/templates/address_contract/index.html.eex:72
msgid "Copy Source Code"
msgstr ""
@ -1480,12 +1481,13 @@ msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:110
#: lib/block_scout_web/templates/layout/_topnav.html.eex:114
#: lib/block_scout_web/templates/layout/_topnav.html.eex:96
#: lib/block_scout_web/templates/layout/_topnav.html.eex:100
msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:35
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:45
msgid "EVM Version"
msgstr ""
@ -1516,20 +1518,11 @@ msgid "Decompiler version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:45
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77
msgid "Optimization runs"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:178
msgid "ERC-20"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:178
msgid "ERC-721"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/index.html.eex:4
msgid "API Documentation"
@ -1546,14 +1539,14 @@ msgid "View All Transactions"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:210
#: lib/block_scout_web/templates/transaction/overview.html.eex:214
msgid "Gas"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:24
#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7
#: lib/block_scout_web/views/transaction_view.ex:265
#: lib/block_scout_web/views/transaction_view.ex:276
msgid "Raw Trace"
msgstr ""
@ -1592,27 +1585,27 @@ msgid "Block Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
#: lib/block_scout_web/templates/address_contract/index.html.eex:112
msgid "Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:75
#: lib/block_scout_web/templates/address_contract/index.html.eex:104
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:85
#: lib/block_scout_web/templates/address_contract/index.html.eex:114
msgid "Copy Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:76
#: lib/block_scout_web/templates/address_contract/index.html.eex:105
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
@ -1699,11 +1692,6 @@ msgstr ""
msgid " Token Transfer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:27
msgid "There is no decompilded contracts for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:14
msgid " is recommended."
@ -1729,6 +1717,11 @@ msgstr ""
msgid "However, in general, the"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:27
msgid "There is no decompilded contracts for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:7
msgid "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found "
@ -1748,3 +1741,59 @@ msgstr ""
#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:9
msgid "here."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
msgid "CSV"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:11
msgid "Change Network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:51
msgid "Constructor arguments"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:58
msgid "External libraries"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:24
msgid "Favorites"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:22
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:18
msgid "Search network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Testnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:12
msgid "Use the search box to find a hosted network, or select from the list of available networks below."
msgstr ""

@ -49,7 +49,7 @@ msgid "%{subnetwork} Explorer - BlockScout"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:133
#: lib/block_scout_web/views/transaction_view.ex:144
msgid "(Awaiting internal transactions for status)"
msgstr ""
@ -105,13 +105,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:4
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:59
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:60
msgid "Addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:23
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:21
#: lib/block_scout_web/views/address_internal_transaction_view.ex:8
#: lib/block_scout_web/views/address_transaction_view.ex:8
msgid "All"
@ -210,8 +211,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37
#: lib/block_scout_web/templates/address/overview.html.eex:144
#: lib/block_scout_web/templates/address/overview.html.eex:152
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:106
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:114
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:107
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:115
msgid "Close"
msgstr ""
@ -257,7 +258,7 @@ msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:53
#: lib/block_scout_web/templates/address_contract/index.html.eex:82
msgid "Contract ABI"
msgstr ""
@ -275,12 +276,12 @@ msgid "Contract Address Pending"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:210
#: lib/block_scout_web/views/transaction_view.ex:221
msgid "Contract Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:209
#: lib/block_scout_web/views/transaction_view.ex:220
msgid "Contract Creation"
msgstr ""
@ -295,7 +296,7 @@ msgid "Contract name:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:41
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
msgid "Contract source code"
msgstr ""
@ -363,12 +364,12 @@ msgid "Error trying to fetch balances."
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:137
#: lib/block_scout_web/views/transaction_view.ex:148
msgid "Error: %{reason}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:135
#: lib/block_scout_web/views/transaction_view.ex:146
msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
@ -378,7 +379,7 @@ msgstr ""
#: lib/block_scout_web/templates/layout/app.html.eex:55
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/_tile.html.eex:30
#: lib/block_scout_web/templates/transaction/overview.html.eex:192
#: lib/block_scout_web/templates/transaction/overview.html.eex:196
#: lib/block_scout_web/views/wei_helpers.ex:78
msgid "Ether"
msgstr "POA"
@ -473,7 +474,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6
#: lib/block_scout_web/views/address_view.ex:306
#: lib/block_scout_web/views/transaction_view.ex:263
#: lib/block_scout_web/views/transaction_view.ex:274
msgid "Internal Transactions"
msgstr ""
@ -490,7 +491,7 @@ msgid "Less than"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:220
#: lib/block_scout_web/templates/transaction/overview.html.eex:224
msgid "Limit"
msgstr ""
@ -500,7 +501,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8
#: lib/block_scout_web/views/address_view.ex:312
#: lib/block_scout_web/views/transaction_view.ex:264
#: lib/block_scout_web/views/transaction_view.ex:275
msgid "Logs"
msgstr ""
@ -512,7 +513,7 @@ msgid "Market Cap"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:118
#: lib/block_scout_web/views/transaction_view.ex:129
msgid "Max of"
msgstr ""
@ -576,7 +577,7 @@ msgid "OUT"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_contract/index.html.eex:40
msgid "Optimization enabled"
msgstr ""
@ -602,8 +603,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44
#: lib/block_scout_web/views/transaction_view.ex:132
#: lib/block_scout_web/views/transaction_view.ex:166
#: lib/block_scout_web/views/transaction_view.ex:143
#: lib/block_scout_web/views/transaction_view.ex:177
msgid "Pending"
msgstr ""
@ -626,8 +627,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:33
#: lib/block_scout_web/templates/address/overview.html.eex:143
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:35
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:105
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:106
msgid "QR Code"
msgstr ""
@ -666,8 +667,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:116
#: lib/block_scout_web/templates/layout/_topnav.html.eex:133
#: lib/block_scout_web/templates/layout/_topnav.html.eex:102
#: lib/block_scout_web/templates/layout/_topnav.html.eex:119
msgid "Search"
msgstr ""
@ -684,13 +685,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:34
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:37
msgid "Show QR Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8
#: lib/block_scout_web/views/transaction_view.ex:134
#: lib/block_scout_web/views/transaction_view.ex:145
msgid "Success"
msgstr ""
@ -743,7 +744,7 @@ msgid "There are no tokens."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:62
#: lib/block_scout_web/templates/address_transaction/index.html.eex:64
msgid "There are no transactions for this address."
msgstr ""
@ -795,7 +796,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:5
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:208
#: lib/block_scout_web/views/transaction_view.ex:219
msgid "Token Transfer"
msgstr ""
@ -805,7 +806,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7
#: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:262
#: lib/block_scout_web/views/transaction_view.ex:273
msgid "Token Transfers"
msgstr ""
@ -834,7 +835,7 @@ msgid "Total Difficulty"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:74
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:75
msgid "Total Supply"
msgstr ""
@ -845,7 +846,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:3
#: lib/block_scout_web/views/transaction_view.ex:211
#: lib/block_scout_web/views/transaction_view.ex:222
msgid "Transaction"
msgstr ""
@ -881,7 +882,7 @@ msgid "Transactions sent"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:60
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:61
msgid "Transfers"
msgstr ""
@ -907,7 +908,7 @@ msgid "Unique Token"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:214
#: lib/block_scout_web/templates/transaction/overview.html.eex:218
msgid "Used"
msgstr ""
@ -927,7 +928,7 @@ msgid "Validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:192
#: lib/block_scout_web/templates/transaction/overview.html.eex:196
msgid "Value"
msgstr ""
@ -943,7 +944,7 @@ msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:54
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:55
msgid "View Contract"
msgstr ""
@ -1048,7 +1049,7 @@ msgid "Self-Destruct"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:62
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:63
msgid "Decimals"
msgstr ""
@ -1210,7 +1211,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_logs/index.html.eex:21
#: lib/block_scout_web/templates/address_token/index.html.eex:13
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:20
#: lib/block_scout_web/templates/address_transaction/index.html.eex:57
#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_validation/index.html.eex:22
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/chain/show.html.eex:91
@ -1404,17 +1405,17 @@ msgid "Support"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:55
#: lib/block_scout_web/templates/address_contract/index.html.eex:84
msgid "Copy ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:71
#: lib/block_scout_web/templates/address_contract/index.html.eex:100
msgid "Copy Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:43
#: lib/block_scout_web/templates/address_contract/index.html.eex:72
msgid "Copy Source Code"
msgstr ""
@ -1480,12 +1481,13 @@ msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:110
#: lib/block_scout_web/templates/layout/_topnav.html.eex:114
#: lib/block_scout_web/templates/layout/_topnav.html.eex:96
#: lib/block_scout_web/templates/layout/_topnav.html.eex:100
msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:35
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:45
msgid "EVM Version"
msgstr ""
@ -1516,20 +1518,11 @@ msgid "Decompiler version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:45
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77
msgid "Optimization runs"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:178
msgid "ERC-20"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:178
msgid "ERC-721"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/index.html.eex:4
msgid "API Documentation"
@ -1546,14 +1539,14 @@ msgid "View All Transactions"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:210
#: lib/block_scout_web/templates/transaction/overview.html.eex:214
msgid "Gas"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:24
#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7
#: lib/block_scout_web/views/transaction_view.ex:265
#: lib/block_scout_web/views/transaction_view.ex:276
msgid "Raw Trace"
msgstr ""
@ -1592,27 +1585,27 @@ msgid "Block Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
#: lib/block_scout_web/templates/address_contract/index.html.eex:112
msgid "Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:75
#: lib/block_scout_web/templates/address_contract/index.html.eex:104
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:85
#: lib/block_scout_web/templates/address_contract/index.html.eex:114
msgid "Copy Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:76
#: lib/block_scout_web/templates/address_contract/index.html.eex:105
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
@ -1699,11 +1692,6 @@ msgstr ""
msgid " Token Transfer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:27
msgid "There is no decompilded contracts for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:14
msgid " is recommended."
@ -1714,7 +1702,7 @@ msgstr ""
msgid "Anything not in this list is not supported. Click on the method to be taken to the documentation for that method, and check the notes section for any potential differences."
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:4
msgid "ETH RPC API Documentation"
msgstr ""
@ -1729,6 +1717,11 @@ msgstr ""
msgid "However, in general, the"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:27
msgid "There is no decompilded contracts for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:7
msgid "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found "
@ -1744,7 +1737,63 @@ msgstr ""
msgid "custom RPC"
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:9
msgid "here."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
msgid "CSV"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:11
msgid "Change Network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:51
msgid "Constructor arguments"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:58
msgid "External libraries"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:24
msgid "Favorites"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:22
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:18
msgid "Search network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Testnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:12
msgid "Use the search box to find a hosted network, or select from the list of available networks below."
msgstr ""

@ -132,4 +132,40 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do
end)
end
end
describe "GET token_transfers_csv/2" do
test "exports token transfers to csv", %{conn: conn} do
address = insert(:address)
transaction =
:transaction
|> insert(from_address: address)
|> with_block()
insert(:token_transfer, transaction: transaction, from_address: address)
insert(:token_transfer, transaction: transaction, to_address: address)
conn = get(conn, "/token_transfers_csv", %{"address_id" => to_string(address.hash)})
assert conn.resp_body |> String.split("\n") |> Enum.count() == 4
end
end
describe "GET transactions_csv/2" do
test "download csv file with transactions", %{conn: conn} do
address = insert(:address)
:transaction
|> insert(from_address: address)
|> with_block()
:transaction
|> insert(from_address: address)
|> with_block()
conn = get(conn, "/transactions_csv", %{"address_id" => to_string(address.hash)})
assert conn.resp_body |> String.split("\n") |> Enum.count() == 4
end
end
end

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

@ -38,6 +38,9 @@ defmodule BlockScoutWeb.ConnCase do
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()})
end
Supervisor.terminate_child(Explorer.Supervisor, {ConCache, Explorer.Chain.TransactionsCache.cache_name()})
Supervisor.restart_child(Explorer.Supervisor, {ConCache, Explorer.Chain.TransactionsCache.cache_name()})
{:ok, conn: Phoenix.ConnTest.build_conn()}
end
end

@ -27,6 +27,9 @@ defmodule BlockScoutWeb.FeatureCase do
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()})
end
Supervisor.terminate_child(Explorer.Supervisor, {ConCache, Explorer.Chain.TransactionsCache.cache_name()})
Supervisor.restart_child(Explorer.Supervisor, {ConCache, Explorer.Chain.TransactionsCache.cache_name()})
metadata = Phoenix.Ecto.SQL.Sandbox.metadata_for(Explorer.Repo, self())
{:ok, session} = Wallaby.start_session(metadata: metadata)
session = Wallaby.Browser.resize_window(session, 1200, 800)

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

@ -6,7 +6,18 @@ defmodule Explorer.Application do
use Application
alias Explorer.Admin
alias Explorer.Chain.{BlockCountCache, BlockNumberCache, BlocksCache, NetVersionCache, TransactionCountCache}
alias Explorer.Chain.{
BlockCountCache,
BlockNumberCache,
BlocksCache,
NetVersionCache,
TransactionCountCache,
TransactionsCache
}
alias Explorer.Chain.Supply.RSK
alias Explorer.Market.MarketHistoryCache
alias Explorer.Repo.PrometheusLogger
@ -34,7 +45,9 @@ defmodule Explorer.Application do
{BlockCountCache, []},
con_cache_child_spec(BlocksCache.cache_name()),
con_cache_child_spec(NetVersionCache.cache_name()),
con_cache_child_spec(MarketHistoryCache.cache_name())
con_cache_child_spec(MarketHistoryCache.cache_name()),
con_cache_child_spec(RSK.cache_name(), ttl_check_interval: :timer.minutes(1), global_ttl: :timer.minutes(30)),
con_cache_child_spec(TransactionsCache.cache_name())
]
children = base_children ++ configurable_children()
@ -83,14 +96,13 @@ defmodule Explorer.Application do
]
end
defp con_cache_child_spec(name) do
defp con_cache_child_spec(name, params \\ [ttl_check_interval: false]) do
params = Keyword.put(params, :name, name)
Supervisor.child_spec(
{
ConCache,
[
name: name,
ttl_check_interval: false
]
params
},
id: {ConCache, name}
)

@ -46,6 +46,7 @@ defmodule Explorer.Chain do
TokenTransfer,
Transaction,
TransactionCountCache,
TransactionsCache,
Wei
}
@ -1052,7 +1053,7 @@ defmodule Explorer.Chain do
when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
fetch_transactions()
Transaction
|> where(hash: ^hash)
|> join_associations(necessity_by_association)
|> Repo.one()
@ -1948,12 +1949,29 @@ defmodule Explorer.Chain do
@spec recent_collated_transactions([paging_options | necessity_by_association_option]) :: [Transaction.t()]
def recent_collated_transactions(options \\ []) when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
options
|> Keyword.get(:paging_options, @default_paging_options)
if is_nil(paging_options.key) do
paging_options.page_size
|> TransactionsCache.take_enough()
|> case do
nil ->
transactions = fetch_recent_collated_transactions(paging_options, necessity_by_association)
TransactionsCache.update(transactions)
transactions
transactions ->
transactions
end
else
fetch_recent_collated_transactions(paging_options, necessity_by_association)
end
end
def fetch_recent_collated_transactions(paging_options, necessity_by_association) do
paging_options
|> fetch_transactions()
|> where([transaction], not is_nil(transaction.block_number) and not is_nil(transaction.index))
|> order_by([transaction], desc: transaction.block_number, desc: transaction.index)
|> join_associations(necessity_by_association)
|> preload([{:token_transfers, [:token, :from_address, :to_address]}])
|> Repo.all()
@ -2146,7 +2164,7 @@ defmodule Explorer.Chain do
|> page_internal_transaction(paging_options)
|> limit(^paging_options.page_size)
|> order_by([internal_transaction], asc: internal_transaction.index)
|> preload(transaction: :block)
|> preload(:transaction)
|> Repo.all()
end
@ -2580,14 +2598,14 @@ defmodule Explorer.Chain do
internal_transaction.type != ^:call or
fragment(
"""
(SELECT COUNT(sibling.*)
EXISTS (SELECT sibling.*
FROM internal_transactions AS sibling
WHERE sibling.transaction_hash = ?
LIMIT 2
WHERE sibling.transaction_hash = ? AND sibling.index != ?
)
""",
transaction.hash
) > 1
transaction.hash,
internal_transaction.index
)
)
end
@ -2671,14 +2689,19 @@ defmodule Explorer.Chain do
@doc """
Fetches a `t:Token.t/0` by an address hash.
Optionally accepts a list of bindings to preload, just like `Ecto.Query.preload/3`
"""
@spec token_from_address_hash(Hash.Address.t()) :: {:ok, Token.t()} | {:error, :not_found}
def token_from_address_hash(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do
@spec token_from_address_hash(Hash.Address.t(), [Macro.t()]) :: {:ok, Token.t()} | {:error, :not_found}
def token_from_address_hash(
%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash,
preloads \\ []
) do
query =
from(
token in Token,
where: token.contract_address_hash == ^hash,
preload: [{:contract_address, :smart_contract}]
preload: ^preloads
)
case Repo.one(query) do
@ -2702,9 +2725,9 @@ defmodule Explorer.Chain do
@spec transaction_has_token_transfers?(Hash.t()) :: boolean()
def transaction_has_token_transfers?(transaction_hash) do
query = from(tt in TokenTransfer, where: tt.transaction_hash == ^transaction_hash, limit: 1, select: 1)
query = from(tt in TokenTransfer, where: tt.transaction_hash == ^transaction_hash)
Repo.one(query) != nil
Repo.exists?(query)
end
@spec address_tokens_with_balance(Hash.Address.t(), [any()]) :: []
@ -2923,7 +2946,7 @@ defmodule Explorer.Chain do
end
@spec transaction_token_transfer_type(Transaction.t()) ::
{:erc20, TokenTransfer.t()} | {:erc721, TokenTransfer.t()} | nil
:erc20 | :erc721 | :token_transfer | nil
def transaction_token_transfer_type(
%Transaction{
status: :ok,
@ -2933,8 +2956,19 @@ defmodule Explorer.Chain do
} = transaction
) do
zero_wei = %Wei{value: Decimal.new(0)}
result = find_token_transfer_type(transaction, input, value)
if is_nil(result) && Enum.count(transaction.token_transfers) > 0 && value == zero_wei,
do: :token_transfer,
else: result
rescue
_ -> nil
end
transaction = Repo.preload(transaction, token_transfers: :token)
def transaction_token_transfer_type(_), do: nil
defp find_token_transfer_type(transaction, input, value) do
zero_wei = %Wei{value: Decimal.new(0)}
# https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC721/ERC721.sol#L35
case {to_string(input), value} do
@ -2959,6 +2993,9 @@ defmodule Explorer.Chain do
find_erc721_token_transfer(transaction.token_transfers, {from_address, to_address})
{"0xf907fc5b" <> _params, ^zero_wei} ->
:erc20
# check for ERC 20 or for old ERC 721 token versions
{unquote(TokenTransfer.transfer_function_signature()) <> params, ^zero_wei} ->
types = [:address, {:uint, 256}]
@ -2972,34 +3009,31 @@ defmodule Explorer.Chain do
_ ->
nil
end
rescue
_ -> nil
end
def transaction_token_transfer_type(_), do: nil
defp find_erc721_token_transfer(token_transfers, {from_address, to_address}) do
token_transfer =
Enum.find(token_transfers, fn token_transfer ->
token_transfer.from_address_hash.bytes == from_address && token_transfer.to_address_hash.bytes == to_address
end)
if token_transfer, do: {:erc721, token_transfer}
if token_transfer, do: :erc721
end
defp find_erc721_or_erc20_token_transfer(token_transfers, {address, decimal_value}) do
token_transfer =
Enum.find(token_transfers, fn token_transfer ->
token_transfer.to_address_hash.bytes == address &&
(token_transfer.amount == decimal_value || token_transfer.token_id)
token_transfer.to_address_hash.bytes == address && token_transfer.amount == decimal_value
end)
if token_transfer do
case token_transfer.token do
%Token{type: "ERC-20"} -> {:erc20, token_transfer}
%Token{type: "ERC-721"} -> {:erc721, token_transfer}
%Token{type: "ERC-20"} -> :erc20
%Token{type: "ERC-721"} -> :erc721
_ -> nil
end
else
:erc20
end
end

@ -0,0 +1,119 @@
defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
@moduledoc """
Exports token transfers to a csv file.
"""
alias Explorer.{Chain, PagingOptions}
alias Explorer.Chain.{Address, TokenTransfer, Transaction}
alias NimbleCSV.RFC4180
@necessity_by_association [
necessity_by_association: %{
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional,
[token_transfers: :token] => :optional,
[token_transfers: :to_address] => :optional,
[token_transfers: :from_address] => :optional,
[token_transfers: :token_contract_address] => :optional,
:block => :required
}
]
@page_size 150
@paging_options %PagingOptions{page_size: @page_size + 1}
def export(address) do
address
|> fetch_all_transactions(@paging_options)
|> to_token_transfers()
|> to_csv_format(address)
|> dump_to_stream()
end
defp fetch_all_transactions(address, paging_options, acc \\ []) do
options = Keyword.merge(@necessity_by_association, paging_options: paging_options)
transactions =
address
|> Chain.address_to_transactions_with_rewards(options)
|> Enum.filter(fn transaction -> Enum.count(transaction.token_transfers) > 0 end)
new_acc = transactions ++ acc
case Enum.split(transactions, @page_size) do
{_transactions, [%Transaction{block_number: block_number, index: index}]} ->
new_paging_options = %{@paging_options | key: {block_number, index}}
fetch_all_transactions(address, new_paging_options, new_acc)
{_, []} ->
new_acc
end
end
defp to_token_transfers(transactions) do
transactions
|> Enum.flat_map(fn transaction ->
transaction.token_transfers
|> Enum.map(fn transfer -> %{transfer | transaction: transaction} end)
end)
end
defp dump_to_stream(transactions) do
transactions
|> RFC4180.dump_to_stream()
end
defp to_csv_format(token_transfers, address) do
row_names = [
"TxHash",
"BlockNumber",
"UnixTimestamp",
"FromAddress",
"ToAddress",
"TokenContractAddress",
"Type",
"TokenSymbol",
"TokensTransferred",
"TransactionFee",
"Status",
"ErrCode"
]
token_transfer_lists =
token_transfers
|> Stream.map(fn token_transfer ->
[
to_string(token_transfer.transaction_hash),
token_transfer.transaction.block_number,
token_transfer.transaction.block.timestamp,
token_transfer.from_address |> to_string() |> String.downcase(),
token_transfer.to_address |> to_string() |> String.downcase(),
token_transfer.token_contract_address |> to_string() |> String.downcase(),
type(token_transfer, address),
token_transfer.token.symbol,
token_transfer.amount,
fee(token_transfer.transaction),
token_transfer.transaction.status,
token_transfer.transaction.error
]
end)
Stream.concat([row_names], token_transfer_lists)
end
defp type(%TokenTransfer{from_address_hash: from_address}, %Address{hash: from_address}), do: "OUT"
defp type(%TokenTransfer{to_address_hash: to_address}, %Address{hash: to_address}), do: "IN"
defp type(_, _), do: ""
defp fee(transaction) do
transaction
|> Chain.fee(:wei)
|> case do
{:actual, value} -> value
{:maximum, value} -> "Max of #{value}"
end
end
end

@ -0,0 +1,139 @@
defmodule Explorer.Chain.AddressTransactionCsvExporter do
@moduledoc """
Exports transactions to a csv file.
"""
import Ecto.Query,
only: [
from: 2
]
alias Explorer.{Chain, Market, PagingOptions, Repo}
alias Explorer.Market.MarketHistory
alias Explorer.Chain.{Address, Transaction, Wei}
alias Explorer.ExchangeRates.Token
alias NimbleCSV.RFC4180
@necessity_by_association [
necessity_by_association: %{
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional,
[token_transfers: :token] => :optional,
[token_transfers: :to_address] => :optional,
[token_transfers: :from_address] => :optional,
[token_transfers: :token_contract_address] => :optional,
:block => :required
}
]
@page_size 150
@paging_options %PagingOptions{page_size: @page_size + 1}
@spec export(Address.t()) :: Enumerable.t()
def export(address) do
exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
address
|> fetch_all_transactions(@paging_options)
|> to_csv_format(address, exchange_rate)
|> dump_to_stream()
end
defp fetch_all_transactions(address, paging_options, acc \\ []) do
options = Keyword.merge(@necessity_by_association, paging_options: paging_options)
transactions = Chain.address_to_transactions_with_rewards(address, options)
new_acc = transactions ++ acc
case Enum.split(transactions, @page_size) do
{_transactions, [%Transaction{block_number: block_number, index: index}]} ->
new_paging_options = %{@paging_options | key: {block_number, index}}
fetch_all_transactions(address, new_paging_options, new_acc)
{_, []} ->
new_acc
end
end
defp dump_to_stream(transactions) do
transactions
|> RFC4180.dump_to_stream()
end
defp to_csv_format(transactions, address, exchange_rate) do
row_names = [
"TxHash",
"BlockNumber",
"UnixTimestamp",
"FromAddress",
"ToAddress",
"ContractAddress",
"Type",
"Value",
"Fee",
"Status",
"ErrCode",
"CurrentPrice",
"TxDateOpeningPrice",
"TxDateClosingPrice"
]
transaction_lists =
transactions
|> Stream.map(fn transaction ->
{opening_price, closing_price} = price_at_date(transaction.block.timestamp)
[
to_string(transaction.hash),
transaction.block_number,
transaction.block.timestamp,
to_string(transaction.from_address),
to_string(transaction.to_address),
to_string(transaction.created_contract_address),
type(transaction, address),
Wei.to(transaction.value, :wei),
fee(transaction),
transaction.status,
transaction.error,
exchange_rate.usd_value,
opening_price,
closing_price
]
end)
Stream.concat([row_names], transaction_lists)
end
defp type(%Transaction{from_address_hash: from_address}, %Address{hash: from_address}), do: "OUT"
defp type(%Transaction{to_address_hash: to_address}, %Address{hash: to_address}), do: "IN"
defp type(_, _), do: ""
defp fee(transaction) do
transaction
|> Chain.fee(:wei)
|> case do
{:actual, value} -> value
{:maximum, value} -> "Max of #{value}"
end
end
defp price_at_date(datetime) do
date = DateTime.to_date(datetime)
query =
from(
mh in MarketHistory,
where: mh.date == ^date
)
case Repo.one(query) do
nil -> {nil, nil}
price -> {price.opening_price, price.closing_price}
end
end
end

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

@ -90,8 +90,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
conflict_target: :hash,
on_conflict: on_conflict,
for: Transaction,
returning:
~w(block_number index hash internal_transactions_indexed_at block_hash old_block_hash nonce from_address_hash created_contract_address_hash)a,
returning: true,
timeout: timeout,
timestamps: timestamps
)

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

@ -6,14 +6,20 @@ defmodule Explorer.Chain.Supply.RSK do
use Explorer.Chain.Supply
import Ecto.Query, only: [from: 2]
import EthereumJSONRPC, only: [integer_to_quantity: 1]
alias EthereumJSONRPC.FetchedBalances
alias Explorer.Chain.Address.CoinBalance
alias Explorer.Chain.{Block, Wei}
alias Explorer.ExchangeRates.Token
alias Explorer.{Market, Repo}
alias Explorer.Chain.{Block, BlockNumberCache, Wei}
alias Explorer.Repo
@cache_name :rsk_balance
@balance_key :balance
def market_cap(exchange_rate) do
circulating() * exchange_rate.usd_value
btc = circulating()
Decimal.mult(btc, exchange_rate.usd_value)
end
@doc "Equivalent to getting the circulating value "
@ -60,13 +66,16 @@ defmodule Explorer.Chain.Supply.RSK do
|> Timex.shift(days: i)
|> Timex.to_date()
case Map.get(by_day, date) do
nil ->
{Map.put(days, date, last), last}
cur_value =
case Map.get(by_day, date) do
nil ->
last
value ->
value.value
end
value ->
{Map.put(days, date, value.value), value.value}
end
{Map.put(days, date, calculate_value(cur_value)), cur_value}
end)
|> elem(0)
@ -74,18 +83,48 @@ defmodule Explorer.Chain.Supply.RSK do
end
def circulating do
query =
from(balance in CoinBalance,
join: block in Block,
on: block.number == balance.block_number,
where: block.consensus == true,
where: balance.address_hash == ^"0x0000000000000000000000000000000001000006",
order_by: [desc: block.timestamp],
limit: 1,
select: balance.value
)
value = ConCache.get(@cache_name, @balance_key)
if is_nil(value) do
updated_value = fetch_circulating_value()
Repo.one(query) || wei!(0)
ConCache.put(@cache_name, @balance_key, updated_value)
updated_value
else
value
end
end
def cache_name, do: @cache_name
defp fetch_circulating_value do
max_number = BlockNumberCache.max_number()
params = [
%{block_quantity: integer_to_quantity(max_number), hash_data: "0x0000000000000000000000000000000001000006"}
]
json_rpc_named_argumens = Application.get_env(:explorer, :json_rpc_named_arguments)
case EthereumJSONRPC.fetch_balances(params, json_rpc_named_argumens) do
{:ok,
%FetchedBalances{
errors: [],
params_list: [
%{
address_hash: "0x0000000000000000000000000000000001000006",
value: value
}
]
}} ->
calculate_value(value)
_ ->
Decimal.new(0)
end
rescue
_ -> Decimal.new(0)
end
defp wei!(value) do
@ -94,10 +133,15 @@ defmodule Explorer.Chain.Supply.RSK do
end
def total do
21_000_000
Decimal.new(21_000_000)
end
def exchange_rate do
Market.get_exchange_rate(Explorer.coin()) || Token.null()
defp calculate_value(val) do
sub =
val
|> Decimal.new()
|> Decimal.div(Decimal.new(1_000_000_000_000_000_000))
Decimal.sub(total(), sub)
end
end

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

@ -0,0 +1,143 @@
defmodule Explorer.Chain.TransactionsCache do
@moduledoc """
Caches the latest imported transactions
"""
alias Explorer.Chain.Transaction
alias Explorer.Repo
@transactions_ids_key "transactions_ids"
@cache_name :transactions
@max_size 51
@preloads [
:block,
created_contract_address: :names,
from_address: :names,
to_address: :names,
token_transfers: :token,
token_transfers: :from_address,
token_transfers: :to_address
]
@spec cache_name :: atom()
def cache_name, do: @cache_name
@doc """
Fetches a transaction from its id ({block_number, index}), returns nil if not found
"""
@spec get({non_neg_integer(), non_neg_integer()}) :: Transaction.t() | nil
def get(id), do: ConCache.get(@cache_name, id)
@doc """
Return the current number of transactions stored
"""
@spec size :: non_neg_integer()
def size, do: Enum.count(transactions_ids())
@doc """
Checks if there are enough transactions stored
"""
@spec enough?(non_neg_integer()) :: boolean()
def enough?(amount) do
amount <= size()
end
@doc """
Checks if the number of transactions stored is already the max allowed
"""
@spec full? :: boolean()
def full? do
@max_size <= size()
end
@doc "Returns the list ids of the transactions currently stored"
@spec transactions_ids :: [{non_neg_integer(), non_neg_integer()}]
def transactions_ids do
ConCache.get(@cache_name, @transactions_ids_key) || []
end
@doc "Returns all the stored transactions"
@spec all :: [Transaction.t()]
def all, do: Enum.map(transactions_ids(), &get(&1))
@doc "Returns the `n` most recent transactions stored"
@spec take(integer()) :: [Transaction.t()]
def take(amount) do
transactions_ids()
|> Enum.take(amount)
|> Enum.map(&get(&1))
end
@doc """
Returns the `n` most recent transactions, unless there are not as many stored,
in which case returns `nil`
"""
@spec take_enough(integer()) :: [Transaction.t()] | nil
def take_enough(amount) do
if enough?(amount), do: take(amount)
end
@doc """
Adds a transaction (or a list of transactions).
If the cache is already full, the transaction will be only stored if it can take
the place of a less recent one.
NOTE: each transaction is inserted atomically
"""
@spec update([Transaction.t()] | Transaction.t() | nil) :: :ok
def update(transactions) when is_nil(transactions), do: :ok
def update(transactions) when is_list(transactions) do
Enum.map(transactions, &update(&1))
end
def update(transaction) do
ConCache.isolated(@cache_name, @transactions_ids_key, fn ->
transaction_id = {transaction.block_number, transaction.index}
ids = transactions_ids()
if full?() do
{init, [min]} = Enum.split(ids, -1)
cond do
transaction_id < min ->
:ok
transaction_id > min ->
insert_transaction(transaction_id, transaction, init)
ConCache.delete(@cache_name, min)
transaction_id == min ->
put_transaction(transaction_id, transaction)
end
else
insert_transaction(transaction_id, transaction, ids)
end
end)
end
defp insert_transaction(transaction_id, transaction, ids) do
put_transaction(transaction_id, transaction)
ConCache.put(@cache_name, @transactions_ids_key, insert_sorted(transaction_id, ids))
end
defp put_transaction(transaction_id, transaction) do
full_transaction = Repo.preload(transaction, @preloads)
ConCache.put(@cache_name, transaction_id, full_transaction)
end
defp insert_sorted(id, ids) do
case ids do
[] ->
[id]
[head | tail] ->
cond do
head > id -> [head | insert_sorted(id, tail)]
head < id -> [id | ids]
head == id -> ids
end
end
end
end

@ -66,13 +66,23 @@ defmodule Explorer.Counters.AverageBlockTime do
from(block in Block,
limit: 100,
offset: 0,
order_by: [desc: block.number, desc: block.timestamp],
order_by: [desc: block.number],
select: {block.number, block.timestamp}
)
query =
if Application.get_env(:explorer, :include_uncles_in_average_block_time) do
timestamps_query
else
from(block in timestamps_query,
where: block.consensus == true
)
end
timestamps =
timestamps_query
query
|> Repo.all()
|> Enum.sort_by(fn {_, timestamp} -> timestamp end, &>=/2)
|> Enum.map(fn {number, timestamp} ->
{number, DateTime.to_unix(timestamp, :millisecond)}
end)

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

@ -70,7 +70,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
compiler_version = Keyword.fetch!(params, :compiler_version)
code = Keyword.fetch!(params, :code)
optimize = Keyword.fetch!(params, :optimize)
optimization_runs = params |> Keyword.get(:optimization_runs, 200) |> Integer.to_string()
optimization_runs = optimization_runs(params)
evm_version = Keyword.get(params, :evm_version, List.last(allowed_evm_versions()))
external_libs = Keyword.get(params, :external_libs, %{})
@ -91,7 +91,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
"node",
[
Application.app_dir(:explorer, "priv/compile_solc.js"),
code,
create_source_file(code),
compiler_version,
optimize_value(optimize),
optimization_runs,
@ -162,4 +162,22 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
defp optimize_value(true), do: "1"
defp optimize_value("true"), do: "1"
defp optimization_runs(params) do
value = params |> Keyword.get(:optimization_runs, "200")
if is_binary(value) do
value
else
"#{value}"
end
end
defp create_source_file(source) do
{:ok, path} = Briefly.create()
File.write!(path, source)
path
end
end

@ -107,6 +107,13 @@ defmodule Explorer.SmartContract.Verifier do
|> Enum.reverse()
|> :binary.list_to_bin()
# Solidity >= 0.5.9; https://github.com/ethereum/solidity/blob/aa4ee3a1559ebc0354926af962efb3fcc7dc15bd/docs/metadata.rst
"a265627a7a72305820" <>
<<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> _constructor_arguments ->
extracted
|> Enum.reverse()
|> :binary.list_to_bin()
<<next::binary-size(2)>> <> rest ->
do_extract_bytecode([next | extracted], rest)
end

@ -21,6 +21,14 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
constructor_arguments
end
# Solidity >= 0.5.9; https://github.com/ethereum/solidity/blob/aa4ee3a1559ebc0354926af962efb3fcc7dc15bd/docs/metadata.rst
defp extract_constructor_arguments(
"a265627a7a72305820" <>
<<_::binary-size(64)>> <> "64736f6c6343" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments
) do
constructor_arguments
end
defp extract_constructor_arguments(<<_::binary-size(2)>> <> rest) do
extract_constructor_arguments(rest)
end

@ -66,6 +66,7 @@ defmodule Explorer.Mixfile do
# CSV output for benchee
{:benchee_csv, "~> 0.8.0", only: :test},
{:bypass, "~> 1.0", only: :test},
{:briefly, "~> 0.4", github: "CargoSense/briefly"},
{:comeonin, "~> 4.0"},
{:credo, "1.0.0", only: :test, runtime: false},
# For Absinthe to load data in batches
@ -92,6 +93,7 @@ defmodule Explorer.Mixfile do
{:mock, "~> 0.3.0", only: [:test], runtime: false},
{:mox, "~> 0.4", only: [:test]},
{:poison, "~> 3.1"},
{:nimble_csv, "~> 0.6.0"},
{:postgrex, ">= 0.0.0"},
# For compatibility with `prometheus_process_collector`, which hasn't been updated yet
{:prometheus, "~> 4.0", override: true},

@ -1,6 +1,6 @@
#!/usr/bin/env node
var sourceCode = process.argv[2];
var sourceCodePath = process.argv[2];
var version = process.argv[3];
var optimize = process.argv[4];
var optimizationRuns = parseInt(process.argv[5], 10);
@ -13,6 +13,9 @@ var solc = require('solc')
var compilerSnapshot = require(compilerVersionPath);
var solc = solc.setupMethods(compilerSnapshot);
var fs = require('fs');
var sourceCode = fs.readFileSync(sourceCodePath, 'utf8');
const input = {
language: 'Solidity',
sources: {

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

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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save