Merge branch 'master' into ab-sabe-contract-code-from-genesis-file

pull/2875/head
Victor Baranov 5 years ago committed by GitHub
commit abf08c33b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 140
      .circleci/config.yml
  2. 6
      .tool-versions
  3. 64
      CHANGELOG.md
  4. 10
      apps/block_scout_web/assets/css/components/_dashboard-banner.scss
  5. 7
      apps/block_scout_web/assets/css/theme/_ethercore_variables-non-critical.scss
  6. 84
      apps/block_scout_web/assets/css/theme/_ethercore_variables.scss
  7. 3
      apps/block_scout_web/assets/css/theme/_variables-non-critical.scss
  8. 1
      apps/block_scout_web/assets/css/theme/_variables.scss
  9. 1
      apps/block_scout_web/assets/js/app.js
  10. 18
      apps/block_scout_web/assets/js/chart-loader.js
  11. 6
      apps/block_scout_web/assets/js/lib/currency.js
  12. 54
      apps/block_scout_web/assets/js/lib/market_history_chart.js
  13. 57
      apps/block_scout_web/assets/js/lib/modals.js
  14. 9
      apps/block_scout_web/assets/js/lib/try_eth_api.js
  15. 2
      apps/block_scout_web/assets/js/lib/utils.js
  16. 6
      apps/block_scout_web/assets/js/pages/address/coin_balances.js
  17. 20
      apps/block_scout_web/assets/js/pages/chain.js
  18. 40
      apps/block_scout_web/assets/js/pages/verification_form.js
  19. 7
      apps/block_scout_web/assets/js/socket.js
  20. 52
      apps/block_scout_web/assets/package-lock.json
  21. 4
      apps/block_scout_web/assets/package.json
  22. 10139
      apps/block_scout_web/assets/static/images/ethercore_logo.svg
  23. 5382
      apps/block_scout_web/assets/static/images/ethercore_testnet_logo.svg
  24. 0
      apps/block_scout_web/assets/static/manifest.webmanifest
  25. 7
      apps/block_scout_web/assets/webpack.config.js
  26. 3
      apps/block_scout_web/config/config.exs
  27. 3
      apps/block_scout_web/config/dev.exs
  28. 3
      apps/block_scout_web/config/prod.exs
  29. 12
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex
  30. 4
      apps/block_scout_web/lib/block_scout_web/endpoint.ex
  31. 36
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  32. 2
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  33. 18
      apps/block_scout_web/lib/block_scout_web/router.ex
  34. 6
      apps/block_scout_web/lib/block_scout_web/templates/address/_responsive_hash.html.eex
  35. 4
      apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex
  36. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex
  37. 32
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
  38. 8
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  39. 2
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  40. 30
      apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex
  41. 1
      apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
  42. 8
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  43. 19
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
  44. 102
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
  45. 9
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/rpc_view.ex
  46. 4
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/stats_view.ex
  47. 21
      apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex
  48. 2
      apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex
  49. 5
      apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex
  50. 10
      apps/block_scout_web/mix.exs
  51. 126
      apps/block_scout_web/priv/gettext/default.pot
  52. 126
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  53. 28
      apps/block_scout_web/test/block_scout_web/channels/address_channel_test.exs
  54. 6
      apps/block_scout_web/test/block_scout_web/controllers/address_contract_controller_test.exs
  55. 56
      apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs
  56. 12
      apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs
  57. 4
      apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs
  58. 80
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  59. 214
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
  60. 30
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs
  61. 53
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs
  62. 18
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs
  63. 2
      apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs
  64. 31
      apps/block_scout_web/test/block_scout_web/controllers/transaction_internal_transaction_controller_test.exs
  65. 42
      apps/block_scout_web/test/block_scout_web/controllers/transaction_log_controller_test.exs
  66. 14
      apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs
  67. 28
      apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs
  68. 19
      apps/block_scout_web/test/block_scout_web/features/viewing_app_test.exs
  69. 7
      apps/block_scout_web/test/block_scout_web/features/viewing_blocks_test.exs
  70. 14
      apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs
  71. 22
      apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs
  72. 63
      apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs
  73. 6
      apps/block_scout_web/test/block_scout_web/views/address_view_test.exs
  74. 6
      apps/block_scout_web/test/block_scout_web/views/api_docs_view_test.exs
  75. 4
      apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs
  76. 13
      apps/block_scout_web/test/support/fixture/smart_contract/contract_with_lib.json
  77. 5
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex
  78. 6
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/log.ex
  79. 2
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/parity/trace/action.ex
  80. 4
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/receipts_test.exs
  81. 7
      apps/explorer/config/config.exs
  82. 2
      apps/explorer/lib/explorer/admin/role.ex
  83. 2
      apps/explorer/lib/explorer/application.ex
  84. 381
      apps/explorer/lib/explorer/chain.ex
  85. 27
      apps/explorer/lib/explorer/chain/block.ex
  86. 2
      apps/explorer/lib/explorer/chain/block/range.ex
  87. 28
      apps/explorer/lib/explorer/chain/block/reward.ex
  88. 2
      apps/explorer/lib/explorer/chain/block/reward/address_type.ex
  89. 2
      apps/explorer/lib/explorer/chain/cache/address_sum.ex
  90. 53
      apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex
  91. 2
      apps/explorer/lib/explorer/chain/data.ex
  92. 2
      apps/explorer/lib/explorer/chain/hash/address.ex
  93. 2
      apps/explorer/lib/explorer/chain/hash/full.ex
  94. 2
      apps/explorer/lib/explorer/chain/hash/nonce.ex
  95. 3
      apps/explorer/lib/explorer/chain/import.ex
  96. 130
      apps/explorer/lib/explorer/chain/import/runner/blocks.ex
  97. 293
      apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
  98. 82
      apps/explorer/lib/explorer/chain/import/runner/internal_transactions_indexed_at_blocks.ex
  99. 4
      apps/explorer/lib/explorer/chain/import/runner/logs.ex
  100. 6
      apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex
  101. Some files were not shown because too many files have changed in this diff Show More

@ -3,7 +3,7 @@ jobs:
build:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1-node-browsers
- image: circleci/elixir:1.9.4-node-browsers
environment:
MIX_ENV: test
# match POSTGRES_PASSWORD for postgres image below
@ -63,6 +63,10 @@ jobs:
command: npm install
working_directory: "apps/block_scout_web/assets"
- run:
command: npm rebuild node-sass
working_directory: "apps/block_scout_web/assets"
- save_cache:
key: v7-npm-install-{{ .Branch }}-{{ checksum "apps/block_scout_web/assets/package-lock.json" }}
paths: "apps/block_scout_web/assets/node_modules"
@ -129,7 +133,7 @@ jobs:
check_formatted:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1
- image: circleci/elixir:1.9.4
environment:
MIX_ENV: test
@ -143,7 +147,7 @@ jobs:
credo:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1
- image: circleci/elixir:1.9.4
environment:
MIX_ENV: test
@ -177,7 +181,7 @@ jobs:
dialyzer:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1
- image: circleci/elixir:1.9.4
environment:
MIX_ENV: test
@ -229,7 +233,7 @@ jobs:
eslint:
docker:
# Ensure .tool-versions matches
- image: circleci/node:12.13.0-browsers-legacy
- image: circleci/node:12.14.1-browsers-legacy
working_directory: ~/app
@ -247,7 +251,7 @@ jobs:
gettext:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1
- image: circleci/elixir:1.9.4
environment:
MIX_ENV: test
@ -271,7 +275,7 @@ jobs:
jest:
docker:
# Ensure .tool-versions matches
- image: circleci/node:12.13.0-browsers-legacy
- image: circleci/node:12.14.1-browsers-legacy
working_directory: ~/app
@ -286,7 +290,7 @@ jobs:
release:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1
- image: circleci/elixir:1.9.4
environment:
MIX_ENV: prod
@ -312,7 +316,7 @@ jobs:
sobelow:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1
- image: circleci/elixir:1.9.4
environment:
MIX_ENV: test
@ -336,7 +340,7 @@ jobs:
# test_geth_http_websocket:
# docker:
# # Ensure .tool-versions matches
# - image: circleci/elixir:1.9.1-node-browsers
# - image: circleci/elixir:1.9.4-node-browsers
# environment:
# MIX_ENV: test
# # match POSTGRES_PASSWORD for postgres image below
@ -345,7 +349,7 @@ jobs:
# PGUSER: postgres
# ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Geth.HTTPWebSocket"
# ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Geth"
# - image: circleci/postgres:10.3-alpine
# - image: circleci/postgres:10.10-alpine
# environment:
# # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database
# POSTGRES_DB: explorer_test
@ -390,7 +394,7 @@ jobs:
# test_geth_mox:
# docker:
# # Ensure .tool-versions matches
# - image: circleci/elixir:1.9.1-node-browsers
# - image: circleci/elixir:1.9.4-node-browsers
# environment:
# MIX_ENV: test
# # match POSTGRES_PASSWORD for postgres image below
@ -399,7 +403,7 @@ jobs:
# PGUSER: postgres
# ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Geth.Mox"
# ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox"
# - image: circleci/postgres:10.3-alpine
# - image: circleci/postgres:10.10-alpine
# environment:
# # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database
# POSTGRES_DB: explorer_test
@ -441,64 +445,64 @@ jobs:
# path: cover/excoveralls.html
# - store_test_results:
# path: _build/test/junit
test_parity_http_websocket:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1-node-browsers
environment:
MIX_ENV: test
# match POSTGRES_PASSWORD for postgres image below
PGPASSWORD: postgres
# match POSTGRES_USER for postgres image below
PGUSER: postgres
ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Parity.HTTPWebSocket"
ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Parity"
- image: circleci/postgres:10.3-alpine
environment:
# Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database
POSTGRES_DB: explorer_test
# match PGPASSWORD for elixir image above
POSTGRES_PASSWORD: postgres
# match PGUSER for elixir image above
POSTGRES_USER: postgres
# test_parity_http_websocket:
# docker:
# # Ensure .tool-versions matches
# - image: circleci/elixir:1.9.4-node-browsers
# environment:
# MIX_ENV: test
# # match POSTGRES_PASSWORD for postgres image below
# PGPASSWORD: postgres
# # match POSTGRES_USER for postgres image below
# PGUSER: postgres
# ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Parity.HTTPWebSocket"
# ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Parity"
# - image: circleci/postgres:10.10-alpine
# environment:
# # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database
# POSTGRES_DB: explorer_test
# # match PGPASSWORD for elixir image above
# POSTGRES_PASSWORD: postgres
# # match PGUSER for elixir image above
# POSTGRES_USER: postgres
working_directory: ~/app
# working_directory: ~/app
steps:
- attach_workspace:
at: .
# steps:
# - attach_workspace:
# at: .
- run:
command: ./bin/install_chrome_headless.sh
no_output_timeout: 2400
# - run:
# command: ./bin/install_chrome_headless.sh
# no_output_timeout: 2400
- run: mix local.hex --force
- run: mix local.rebar --force
# - run: mix local.hex --force
# - run: mix local.rebar --force
- run:
name: Wait for DB
command: dockerize -wait tcp://localhost:5432 -timeout 1m
# - run:
# name: Wait for DB
# command: dockerize -wait tcp://localhost:5432 -timeout 1m
- run:
name: mix test --exclude no_parity
command: |
# Don't submit coverage report for forks, but let the build succeed
if [[ -z "$COVERALLS_REPO_TOKEN" ]]; then
mix coveralls.html --exclude no_parity --parallel --umbrella
else
mix coveralls.circle --exclude no_parity --parallel --umbrella ||
# if mix failed, then coveralls_merge won't run, so signal done here and return original exit status
(retval=$? && curl -k https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN -d "payload[build_num]=$CIRCLE_WORKFLOW_WORKSPACE_ID&payload[status]=done" && return $retval)
fi
# - run:
# name: mix test --exclude no_parity
# command: |
# # Don't submit coverage report for forks, but let the build succeed
# if [[ -z "$COVERALLS_REPO_TOKEN" ]]; then
# mix coveralls.html --exclude no_parity --parallel --umbrella
# else
# mix coveralls.circle --exclude no_parity --parallel --umbrella ||
# # if mix failed, then coveralls_merge won't run, so signal done here and return original exit status
# (retval=$? && curl -k https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN -d "payload[build_num]=$CIRCLE_WORKFLOW_WORKSPACE_ID&payload[status]=done" && return $retval)
# fi
- store_artifacts:
path: cover/excoveralls.html
- store_test_results:
path: _build/test/junit
# - store_artifacts:
# path: cover/excoveralls.html
# - store_test_results:
# path: _build/test/junit
test_parity_mox:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1-node-browsers
- image: circleci/elixir:1.9.4-node-browsers
environment:
MIX_ENV: test
# match POSTGRES_PASSWORD for postgres image below
@ -507,7 +511,7 @@ jobs:
PGUSER: postgres
ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Parity.Mox"
ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox"
- image: circleci/postgres:10.3-alpine
- image: circleci/postgres:10.10-alpine
environment:
# Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database
POSTGRES_DB: explorer_test
@ -552,7 +556,7 @@ jobs:
coveralls_merge:
docker:
# Ensure .tool-versions matches
- image: circleci/elixir:1.9.1
- image: circleci/elixir:1.9.4
environment:
MIX_ENV: test
@ -571,7 +575,7 @@ workflows:
# This unfortunately will only fire if all the tests pass because of how `requires` works
- coveralls_merge:
requires:
- test_parity_http_websocket
# - test_parity_http_websocket
- test_parity_mox
# - test_geth_http_websocket
# - test_geth_mox
@ -591,7 +595,7 @@ workflows:
- eslint
- jest
- sobelow
- test_parity_http_websocket
# - test_parity_http_websocket
- test_parity_mox
# - test_geth_http_websocket
# - test_geth_mox
@ -613,9 +617,9 @@ workflows:
- sobelow:
requires:
- build
- test_parity_http_websocket:
requires:
- build
# - test_parity_http_websocket:
# requires:
# - build
- test_parity_mox:
requires:
- build

@ -1,3 +1,3 @@
elixir 1.9.1-otp-22
erlang 22.0
nodejs 10.11.0
elixir 1.9.4
erlang 22.2
nodejs 12.14.1

@ -1,7 +1,68 @@
## Current
### Features
### Fixes
- [#2996](https://github.com/poanetwork/blockscout/pull/2996) - Fix awesomplete lib loading in Firefox
- [#2993](https://github.com/poanetwork/blockscout/pull/2993) - Fix path definition for contract verification endpoint
- [#2990](https://github.com/poanetwork/blockscout/pull/2990) - Fix import of Parity spec file
- [#2989](https://github.com/poanetwork/blockscout/pull/2989) - Introduce API_PATH env var
- [#2988](https://github.com/poanetwork/blockscout/pull/2988) - Fix web manifest accessibility
- [#2967](https://github.com/poanetwork/blockscout/pull/2967) - Fix styles loading for firefox
- [#2950](https://github.com/poanetwork/blockscout/pull/2950) - Add `creationMethod` to `EthereumJSONRPC.Parity.Trace.Action.entry_to_elixir`
- [#2897](https://github.com/poanetwork/blockscout/pull/2897) - remove duplicate indexes
- [#2883](https://github.com/poanetwork/blockscout/pull/2883) - Fix long contracts names
### Chore
- [#2995](https://github.com/poanetwork/blockscout/pull/2995) - Support API_PATH env var in Docker file
## 3.0.0-beta
### Features
- [#2835](https://github.com/poanetwork/blockscout/pull/2835), [#2871](https://github.com/poanetwork/blockscout/pull/2871), [#2872](https://github.com/poanetwork/blockscout/pull/2872), [#2886](https://github.com/poanetwork/blockscout/pull/2886), [#2925](https://github.com/poanetwork/blockscout/pull/2925), [#2936](https://github.com/poanetwork/blockscout/pull/2936), [#2949](https://github.com/poanetwork/blockscout/pull/2949), [#2940](https://github.com/poanetwork/blockscout/pull/2940), [#2958](https://github.com/poanetwork/blockscout/pull/2958) - Add "block_hash" to logs, token_transfers and internal transactions and "pending blocks operations" approach
- [#2975](https://github.com/poanetwork/blockscout/pull/2975) - Refine UX of contracts verification
- [#2926](https://github.com/poanetwork/blockscout/pull/2926) - API endpoint: sum balances except burnt address
- [#2918](https://github.com/poanetwork/blockscout/pull/2918) - Add tokenID for tokentx API action explicitly
### Fixes
- [#2969](https://github.com/poanetwork/blockscout/pull/2969) - Fix contract constructor require msg appearance in constructor arguments encoded view
- [#2964](https://github.com/poanetwork/blockscout/pull/2964) - Fix bug in skipping of constructor arguments in contract verification
- [#2961](https://github.com/poanetwork/blockscout/pull/2961) - Add a guard that addresses is enum in `values` function in `read contract` page
- [#2960](https://github.com/poanetwork/blockscout/pull/2960) - Add BLOCKSCOUT_HOST to docker setup
- [#2956](https://github.com/poanetwork/blockscout/pull/2956) - Add support of 0.6.x version of compiler
- [#2955](https://github.com/poanetwork/blockscout/pull/2955) - Move socket path to env
- [#2938](https://github.com/poanetwork/blockscout/pull/2938) - utf8 copy tx input tooltip
- [#2934](https://github.com/poanetwork/blockscout/pull/2934) - RSK release 1.2.0 breaking changes support
- [#2933](https://github.com/poanetwork/blockscout/pull/2933) - Get rid of deadlock in the query to address_current_token_balance table
- [#2932](https://github.com/poanetwork/blockscout/pull/2932) - fix duplicate websocket connection
- [#2928](https://github.com/poanetwork/blockscout/pull/2928) - Speedup pending block ops int txs to fetch query
- [#2924](https://github.com/poanetwork/blockscout/pull/2924) - Speedup address to logs query
- [#2915](https://github.com/poanetwork/blockscout/pull/2915) - Speedup of blocks_without_reward_query
- [#2914](https://github.com/poanetwork/blockscout/pull/2914) - Reduce execution time of stream_unfetched_token_instances query
- [#2910](https://github.com/poanetwork/blockscout/pull/2910) - Reorganize queries and indexes for internal_transactions table
- [#2908](https://github.com/poanetwork/blockscout/pull/2908) - Fix performance of address page
- [#2906](https://github.com/poanetwork/blockscout/pull/2906) - fix address sum cache
- [#2902](https://github.com/poanetwork/blockscout/pull/2902) - Offset in blocks retrieval for average block time
- [#2900](https://github.com/poanetwork/blockscout/pull/2900) - check fetched instance metadata in multiple places
- [#2899](https://github.com/poanetwork/blockscout/pull/2899) - fix empty buffered task
- [#2887](https://github.com/poanetwork/blockscout/pull/2887) - increase chart loading speed
### Chore
- [#2959](https://github.com/poanetwork/blockscout/pull/2959) - Remove logs from test folder too in the cleaning script
- [#2954](https://github.com/poanetwork/blockscout/pull/2954) - Upgrade absinthe and ecto deps
- [#2947](https://github.com/poanetwork/blockscout/pull/2947) - Upgrade Circle CI postgres Docker image
- [#2946](https://github.com/poanetwork/blockscout/pull/2946) - Fix vulnerable NPM deps
- [#2942](https://github.com/poanetwork/blockscout/pull/2942) - Actualize Docker setup
- [#2896](https://github.com/poanetwork/blockscout/pull/2896) - Disable Parity websockets tests
- [#2873](https://github.com/poanetwork/blockscout/pull/2873) - bump elixir to 1.9.4
## 2.1.1-beta
### Features
- [#2862](https://github.com/poanetwork/blockscout/pull/2862) - Coin total supply from DB API endpoint
- [#2857](https://github.com/poanetwork/blockscout/pull/2857) - Extend getsourcecode API view with new output fields
- [#2822](https://github.com/poanetwork/blockscout/pull/2822) - Estimated address count on the main page, if cache is empty
- [#2821](https://github.com/poanetwork/blockscout/pull/2821) - add autodetection of constructor arguments
- [#2825](https://github.com/poanetwork/blockscout/pull/2825) - separate token transfers and transactions
@ -9,7 +70,6 @@
- [#2791](https://github.com/poanetwork/blockscout/pull/2791) - add ipc client
- [#2449](https://github.com/poanetwork/blockscout/pull/2449) - add ability to send notification events through postgres notify
### Fixes
- [#2864](https://github.com/poanetwork/blockscout/pull/2864) - add token instance metadata type check
- [#2855](https://github.com/poanetwork/blockscout/pull/2855) - Fix favicons load
@ -24,10 +84,12 @@
- [#2800](https://github.com/poanetwork/blockscout/pull/2800) - return not found for not verified contract for token read_contract
- [#2806](https://github.com/poanetwork/blockscout/pull/2806) - Fix blocks fetching on the main page
- [#2803](https://github.com/poanetwork/blockscout/pull/2803) - Fix block validator custom tooltip
- [#2748](https://github.com/poanetwork/blockscout/pull/2748) - Rewrite token updater
- [#2704](https://github.com/poanetwork/blockscout/pull/2704) - refetch null values in token balances
- [#2690](https://github.com/poanetwork/blockscout/pull/2690) - do not stich json rpc config into module for net version cache
### Chore
- [#2878](https://github.com/poanetwork/blockscout/pull/2878) - Decrease loaders showing delay on the main page
- [#2859](https://github.com/poanetwork/blockscout/pull/2859) - Add eth_blockNumber API endpoint to eth_rpc section
- [#2846](https://github.com/poanetwork/blockscout/pull/2846) - Remove networks images preload
- [#2845](https://github.com/poanetwork/blockscout/pull/2845) - Set outline none for nav dropdown item in mobile view (fix for Safari)

@ -53,14 +53,16 @@ $dashboard-banner-chart-axis-font-color: $dashboard-stats-item-value-color !defa
.dashboard-banner-chart {
flex-grow: 1;
margin: 0 0 35px 0;
margin: 15px 0 20px 0;
max-width: 350px;
position: relative;
min-height: 100px;
height: calc(100% - 86px);
@include media-breakpoint-down(md) {
flex-grow: 0;
margin-bottom: 20px;
margin-top: auto;
margin-top: 20px;
margin-bottom: auto;
max-width: 100%;
}
@ -227,4 +229,4 @@ $dashboard-banner-chart-axis-font-color: $dashboard-stats-item-value-color !defa
font-size: 0.9rem;
}
}
}
}

@ -0,0 +1,7 @@
// general
$primary: #3a6ea7;
$secondary: #558ac3;
$tertiary: #3a6ea7;
$additional-font: #bdbdff;
$btn-line-color: $tertiary; // button border and font color && hover bg color

@ -0,0 +1,84 @@
// general
$primary: #3a6ea7;
$secondary: #558ac3;
$tertiary: #3a6ea7;
$additional-font: #bdbdff;
// footer
$footer-background-color: $primary;
$footer-title-color: #fff;
$footer-text-color: $additional-font;
$footer-item-disc-color: $tertiary;
$footer-social-icon-color: #3a6ea7;
// dashboard
$dashboard-line-color-price: $secondary;
$dashboard-line-color-market: $tertiary;
$dashboard-banner-gradient-start: #7ba4d1;
$dashboard-banner-gradient-end: #3a6ea7;
$dashboard-banner-chart-legend-label-color: $additional-font;
$dashboard-stats-item-label-color: $additional-font;
$dashboard-banner-chart-legend-value-color: #fff; // chart labels
$dashboard-stats-item-value-color: #fff; // stat values
$dashboard-stats-item-border-color: $secondary; // stat border
$dashboard-banner-network-plain-container-background-color: #2e5884; // stats bg
// navigation
$header-icon-border-color-hover: $tertiary; // top border on hover
$header-icon-color-hover: $tertiary; // nav icon on hover
// buttons
$btn-line-bg: #fff; // button bg
$btn-line-color: $tertiary; // button border and font color && hover bg color
$btn-copy-color: $tertiary; // btn copy
$btn-qr-color: $tertiary; // btn qr-code
$btn-address-card-icon-color: $tertiary; // btn address color
$btn-contract-color: $tertiary;
//links & tile
$tile-body-a-color: $tertiary;
$tile-type-block-color: $tertiary;
$tile-type-progress-bar-color: $tertiary;
a.tile-title { color: $tertiary !important; }
// card
$card-background-1: $tertiary;
$card-tab-active: $tertiary;
// ETC theme's idiosyncrasies
.layout-container {
.navbar {
box-shadow: 0 0 30px 0 rgba(21, 53, 80, 0.12);
}
.dropdown-item:hover,
.dropdown-item.active,
.dropdown-item:focus {
background-color: rgba($tertiary, .1) !important;
color: $tertiary !important;
}
.dashboard-banner-container {
background-image: linear-gradient(
to bottom,
$dashboard-banner-gradient-start,
$dashboard-banner-gradient-end
);
}
.footer-logo {
filter: brightness(0) invert(1);
}
}
// Badges
$badge-neutral-color: $tertiary;
$badge-neutral-background-color: rgba($tertiary, .1);
$api-text-monospace-color: $tertiary;
// Dark theme
$dark-primary: #8588ff;
$dark-secondary: #4ad7a7;
$dark-primary-alternate: #5b5ed8;

@ -20,4 +20,5 @@
// @import "sokol_variables-non-critical";
// @import "tobalaba_variables-non-critical";
// @import "tomochain_variables-non-critical";
// @import "rsk_variables-non-critical";
// @import "rsk_variables-non-critical";
// @import "ethercore_variables-non-critical";

@ -21,6 +21,7 @@
// @import "tobalaba_variables";
// @import "tomochain_variables";
// @import "rsk_variables";
// @import "ethercore_variables";
// responsive breakpoints
$breakpoint-xs: 320px;

@ -43,7 +43,6 @@ import './lib/currency'
import './lib/from_now'
import './lib/indexing'
import './lib/loading_element'
import './lib/market_history_chart'
import './lib/pending_transactions_toggle'
import './lib/pretty_json'
import './lib/reload_button'

@ -0,0 +1,18 @@
import $ from 'jquery'
import { formatAllUsdValues, updateAllCalculatedUsdValues } from './lib/currency'
import { createMarketHistoryChart } from './lib/market_history_chart'
import { createCoinBalanceHistoryChart } from './lib/coin_balance_history_chart'
(function () {
const dashboardChartElement = $('[data-chart="marketHistoryChart"]')[0]
const coinBalanceHistoryChartElement = $('[data-chart="coinBalanceHistoryChart"]')[0]
if (dashboardChartElement) {
window.dashboardChart = createMarketHistoryChart(dashboardChartElement)
}
if (coinBalanceHistoryChartElement) {
window.coinBalanceHistoryChart = createCoinBalanceHistoryChart(coinBalanceHistoryChartElement)
}
formatAllUsdValues()
updateAllCalculatedUsdValues()
})()

@ -1,8 +1,6 @@
import $ from 'jquery'
import humps from 'humps'
import numeral from 'numeral'
import { BigNumber } from 'bignumber.js'
import socket from '../socket'
export function formatUsdValue (value) {
return `${formatCurrencyValue(value)} USD`
@ -63,7 +61,3 @@ export function updateAllCalculatedUsdValues (usdExchangeRate) {
$('[data-usd-unit-price]').each((i, el) => tryUpdateUnitPriceValues(el, usdExchangeRate))
}
updateAllCalculatedUsdValues()
export const exchangeRateChannel = socket.channel('exchange_rate:new_rate')
exchangeRateChannel.join()
exchangeRateChannel.on('new_rate', (msg) => updateAllCalculatedUsdValues(humps.camelizeKeys(msg).exchangeRate.usdValue))

@ -4,7 +4,6 @@ import humps from 'humps'
import numeral from 'numeral'
import { formatUsdValue } from '../lib/currency'
import sassVariables from '../../css/app.scss'
import { showLoader } from '../lib/utils'
const config = {
type: 'line',
@ -76,16 +75,36 @@ const config = {
}
}
function getDataFromLocalStorage (key) {
const data = window.localStorage.getItem(key)
return data ? JSON.parse(data) : []
}
function setDataToLocalStorage (key, data) {
window.localStorage.setItem(key, JSON.stringify(data))
}
function getPriceData (marketHistoryData) {
return marketHistoryData.map(({ date, closingPrice }) => ({ x: date, y: closingPrice }))
if (marketHistoryData.length === 0) {
return getDataFromLocalStorage('priceData')
}
const data = marketHistoryData.map(({ date, closingPrice }) => ({ x: date, y: closingPrice }))
setDataToLocalStorage('priceData', data)
return data
}
function getMarketCapData (marketHistoryData, availableSupply) {
if (availableSupply !== null && typeof availableSupply === 'object') {
return marketHistoryData.map(({ date, closingPrice }) => ({ x: date, y: closingPrice * availableSupply[date] }))
} else {
return marketHistoryData.map(({ date, closingPrice }) => ({ x: date, y: closingPrice * availableSupply }))
if (marketHistoryData.length === 0) {
return getDataFromLocalStorage('marketCapData')
}
const data = marketHistoryData.map(({ date, closingPrice }) => {
const supply = (availableSupply !== null && typeof availableSupply === 'object')
? availableSupply[date]
: availableSupply
return { x: date, y: closingPrice * supply }
})
setDataToLocalStorage('marketCapData', data)
return data
}
// colors for light and dark theme
@ -123,6 +142,15 @@ class MarketHistoryChart {
}
this.availableSupply = availableSupply
config.data.datasets = [this.price, this.marketCap]
const isChartLoadedKey = 'isChartLoaded'
const isChartLoaded = window.sessionStorage.getItem(isChartLoadedKey) === 'true'
if (isChartLoaded) {
config.options.animation = false
} else {
window.sessionStorage.setItem(isChartLoadedKey, true)
}
this.chart = new Chart(el, config)
}
@ -141,32 +169,24 @@ class MarketHistoryChart {
export function createMarketHistoryChart (el) {
const dataPath = el.dataset.market_history_chart_path
const $chartLoading = $('[data-chart-loading-message]')
const isTimeout = true
const timeoutID = showLoader(isTimeout, $chartLoading)
const $chartError = $('[data-chart-error-message]')
const chart = new MarketHistoryChart(el, 0, [])
$(el).show()
$.getJSON(dataPath, { type: 'JSON' })
.done(data => {
const availableSupply = JSON.parse(data.supply_data)
const marketHistoryData = humps.camelizeKeys(JSON.parse(data.history_data))
$(el).show()
chart.update(availableSupply, marketHistoryData)
})
.fail(() => {
$(el).hide()
$chartError.show()
})
.always(() => {
$chartLoading.hide()
clearTimeout(timeoutID)
})
return chart
}
$('[data-chart-error-message]').on('click', _event => {
$('[data-chart-loading-message]').show()
$('[data-chart-error-message]').hide()
createMarketHistoryChart($('[data-chart="marketHistoryChart"]')[0])
})

@ -1,5 +1,4 @@
import $ from 'jquery'
import Chart from 'chart.js'
$(function () {
$('.js-become-candidate').on('click', function () {
@ -43,34 +42,34 @@ $(function () {
})
function setupStakesProgress (progress, total, modal) {
const stakeProgress = $(`${modal} .js-stakes-progress`)
const primaryColor = $('.btn-full-primary').css('background-color')
const backgroundColors = [
primaryColor,
'rgba(202, 199, 226, 0.5)'
]
const progressBackground = total - progress
// const stakeProgress = $(`${modal} .js-stakes-progress`)
// const primaryColor = $('.btn-full-primary').css('background-color')
// const backgroundColors = [
// primaryColor,
// 'rgba(202, 199, 226, 0.5)'
// ]
// const progressBackground = total - progress
// eslint-disable-next-line no-unused-vars
const myChart = new Chart(stakeProgress, {
type: 'doughnut',
data: {
datasets: [{
data: [progress, progressBackground],
backgroundColor: backgroundColors,
hoverBackgroundColor: backgroundColors,
borderWidth: 0
}]
},
options: {
cutoutPercentage: 80,
legend: {
display: false
},
tooltips: {
enabled: false
}
}
})
// // eslint-disable-next-line no-unused-vars
// const myChart = new window.Chart(stakeProgress, {
// type: 'doughnut',
// data: {
// datasets: [{
// data: [progress, progressBackground],
// backgroundColor: backgroundColors,
// hoverBackgroundColor: backgroundColors,
// borderWidth: 0
// }]
// },
// options: {
// cutoutPercentage: 80,
// legend: {
// display: false
// },
// tooltips: {
// enabled: false
// }
// }
// })
}
})

@ -44,8 +44,9 @@ function parseInput (input) {
}
}
function dropDomain (url) {
return new URL(url).pathname
function composeRequestUrl () {
const url = $('[data-endpoint-url]').attr('data-endpoint-url')
return url
}
$('button[data-try-eth-api-ui-button-type="execute"]').click(event => {
@ -64,10 +65,8 @@ $('button[data-try-eth-api-ui-button-type="execute"]').click(event => {
clickedButton.html(loadingText)
}
const url = $('[data-endpoint-url]').attr('data-endpoint-url')
$.ajax({
url: dropDomain(url),
url: composeRequestUrl(),
type: 'POST',
data: JSON.stringify(formData),
dataType: 'json',

@ -17,7 +17,7 @@ export function showLoader (isTimeout, loader) {
const timeout = setTimeout(function () {
loader.removeAttr('hidden')
loader.show()
}, 1000)
}, 100)
return timeout
} else {
loader.hide()

@ -4,7 +4,6 @@ import humps from 'humps'
import socket from '../../socket'
import { connectElements } from '../../lib/redux_helpers.js'
import { createAsyncLoadStore } from '../../lib/async_listing_load'
import { createCoinBalanceHistoryChart } from '../../lib/coin_balance_history_chart'
export const initialState = {
channelDisconnected: false
@ -61,9 +60,4 @@ if ($('[data-page="coin-balance-history"]').length) {
msg: humps.camelizeKeys(msg)
})
})
const chartContainer = $('[data-chart="coinBalanceHistoryChart"]')[0]
if (chartContainer) {
createCoinBalanceHistoryChart(chartContainer)
}
}

@ -7,11 +7,10 @@ import map from 'lodash/map'
import humps from 'humps'
import numeral from 'numeral'
import socket from '../socket'
import { exchangeRateChannel, formatUsdValue } from '../lib/currency'
import { updateAllCalculatedUsdValues, formatUsdValue } from '../lib/currency'
import { createStore, connectElements } from '../lib/redux_helpers.js'
import { batchChannel, showLoader } from '../lib/utils'
import listMorph from '../lib/list_morph'
import { createMarketHistoryChart } from '../lib/market_history_chart'
const BATCH_THRESHOLD = 6
@ -142,8 +141,8 @@ function withMissingBlocks (reducer) {
let chart
const elements = {
'[data-chart="marketHistoryChart"]': {
load ($el) {
chart = createMarketHistoryChart($el[0])
load () {
chart = window.dashboardChart
},
render ($el, state, oldState) {
if (!chart || (oldState.availableSupply === state.availableSupply && oldState.marketHistoryData === state.marketHistoryData) || !state.availableSupply) return
@ -259,10 +258,15 @@ if ($chainDetailsPage.length) {
loadBlocks(store)
bindBlockErrorMessage(store)
exchangeRateChannel.on('new_rate', (msg) => store.dispatch({
type: 'RECEIVED_NEW_EXCHANGE_RATE',
msg: humps.camelizeKeys(msg)
}))
const exchangeRateChannel = socket.channel('exchange_rate:new_rate')
exchangeRateChannel.join()
exchangeRateChannel.on('new_rate', (msg) => {
updateAllCalculatedUsdValues(humps.camelizeKeys(msg).exchangeRate.usdValue)
store.dispatch({
type: 'RECEIVED_NEW_EXCHANGE_RATE',
msg: humps.camelizeKeys(msg)
})
})
const addressesChannel = socket.channel('addresses:new_address')
addressesChannel.join()

@ -88,6 +88,26 @@ const elements = {
const $contractVerificationPage = $('[data-page="contract-verification"]')
function filterNightlyBuilds (filter) {
var select, options
select = document.getElementById('smart_contract_compiler_version')
options = select.getElementsByTagName('option')
for (const option of options) {
var txtValue = option.textContent || option.innerText
if (filter) {
if (txtValue.toLowerCase().indexOf('nightly') > -1) {
option.style.display = 'none'
} else {
option.style.display = ''
}
} else {
if (txtValue.toLowerCase().indexOf('nightly') > -1) {
option.style.display = ''
}
}
}
}
if ($contractVerificationPage.length) {
const store = createStore(reducer)
const addressHash = $('#smart_contract_address_hash').val()
@ -116,6 +136,10 @@ if ($contractVerificationPage.length) {
})
$(function () {
setTimeout(function () {
$('.nightly-builds-false').trigger('click')
}, 10)
$('.js-btn-add-contract-libraries').on('click', function () {
$('.js-smart-contract-libraries-wrapper').show()
$(this).hide()
@ -129,6 +153,22 @@ if ($contractVerificationPage.length) {
if ($(this).prop('checked')) { $('.constructor-arguments').hide() }
})
$('.nightly-builds-true').on('click', function () {
if ($(this).prop('checked')) { filterNightlyBuilds(false) }
})
$('.nightly-builds-false').on('click', function () {
if ($(this).prop('checked')) { filterNightlyBuilds(true) }
})
$('.optimization-false').on('click', function () {
if ($(this).prop('checked')) { $('.optimization-runs').hide() }
})
$('.optimization-true').on('click', function () {
if ($(this).prop('checked')) { $('.optimization-runs').show() }
})
$('.js-smart-contract-form-reset').on('click', function () {
$('.js-contract-library-form-group').removeClass('active')
$('.js-contract-library-form-group').first().addClass('active')

@ -1,7 +1,12 @@
import { Socket } from 'phoenix'
import { locale } from './locale'
const socket = new Socket('/socket', { params: { locale: locale } })
let websocketRootUrl = process.env.SOCKET_ROOT
if (!websocketRootUrl || websocketRootUrl === '/') {
websocketRootUrl = ''
}
const socket = new Socket(websocketRootUrl + '/socket', { params: { locale: locale } })
socket.connect()
export default socket

@ -5883,9 +5883,9 @@
"dev": true
},
"handlebars": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz",
"integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==",
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.6.0.tgz",
"integrity": "sha512-i1ZUP7Qp2JdkMaFon2a+b0m5geE8Z4ZTLaGkgrObkEd+OkUKyRbRWw4KxuFCoHfdETSY1yf9/574eVoNSiK7pw==",
"dev": true,
"requires": {
"neo-async": "^2.6.0",
@ -11466,9 +11466,9 @@
"dev": true
},
"serialize-javascript": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.0.tgz",
"integrity": "sha512-a/mxFfU00QT88umAJQsNWOnUKckhNCqOl028N48e7wFmo2/EHpTo9Wso+iJJCMrQnmFvcjto5RJdAHEvVhcyUQ==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
"dev": true
},
"set-blocking": {
@ -12672,9 +12672,9 @@
"dev": true
},
"uglify-js": {
"version": "3.6.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz",
"integrity": "sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==",
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.4.tgz",
"integrity": "sha512-tinYWE8X1QfCHxS1lBS8yiDekyhSXOO6R66yNOCdUJeojxxw+PX2BHAz/BWyW7PQ7pkiWVxJfIEbiDxyLWvUGg==",
"dev": true,
"optional": true,
"requires": {
@ -13312,12 +13312,6 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"serialize-javascript": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -13325,20 +13319,31 @@
"dev": true
},
"terser-webpack-plugin": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz",
"integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==",
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
"integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
"dev": true,
"requires": {
"cacache": "^12.0.2",
"find-cache-dir": "^2.1.0",
"is-wsl": "^1.1.0",
"schema-utils": "^1.0.0",
"serialize-javascript": "^1.7.0",
"serialize-javascript": "^2.1.2",
"source-map": "^0.6.1",
"terser": "^4.1.2",
"webpack-sources": "^1.4.0",
"worker-farm": "^1.7.0"
},
"dependencies": {
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
"integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
"dev": true,
"requires": {
"errno": "~0.1.7"
}
}
}
}
}
@ -13586,15 +13591,6 @@
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
"dev": true
},
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
"integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
"dev": true,
"requires": {
"errno": "~0.1.7"
}
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",

@ -8,8 +8,8 @@
"author": "POA Network",
"license": "GPL-3.0",
"engines": {
"node": "9.x",
"npm": "5.x"
"node": "12.x",
"npm": "6.x"
},
"scripts": {
"deploy": "webpack --mode production",

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 762 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 405 KiB

@ -5,6 +5,7 @@ const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { ContextReplacementPlugin } = require('webpack')
const glob = require('glob')
const webpack = require('webpack')
function transpileViewScript(file) {
return {
@ -75,6 +76,7 @@ const appJs =
entry: {
app: './js/app.js',
stakes: './js/pages/stakes.js',
'chart-loader': './js/chart-loader.js',
'non-critical': './css/non-critical.scss'
},
output: {
@ -135,7 +137,10 @@ const appJs =
filename: '../css/[name].css'
}),
new CopyWebpackPlugin([{ from: 'static/', to: '../' }]),
new ContextReplacementPlugin(/moment[\/\\]locale$/, /en/)
new ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
new webpack.DefinePlugin({
'process.env.SOCKET_ROOT': JSON.stringify(process.env.SOCKET_ROOT)
})
]
}

@ -41,7 +41,8 @@ config :block_scout_web, BlockScoutWeb.Endpoint,
url: [
scheme: System.get_env("BLOCKSCOUT_PROTOCOL") || "http",
host: System.get_env("BLOCKSCOUT_HOST") || "localhost",
path: System.get_env("NETWORK_PATH") || "/"
path: System.get_env("NETWORK_PATH") || "/",
api_path: System.get_env("API_PATH") || "/"
],
render_errors: [view: BlockScoutWeb.ErrorView, accepts: ~w(html json)],
pubsub: [name: BlockScoutWeb.PubSub]

@ -21,7 +21,8 @@ config :block_scout_web, BlockScoutWeb.Endpoint,
url: [
scheme: "http",
host: System.get_env("BLOCKSCOUT_HOST") || "localhost",
path: System.get_env("NETWORK_PATH") || "/"
path: System.get_env("NETWORK_PATH") || "/",
api_path: System.get_env("API_PATH") || "/"
],
https: [
port: (port && port + 1) || 4001,

@ -23,7 +23,8 @@ config :block_scout_web, BlockScoutWeb.Endpoint,
scheme: System.get_env("BLOCKSCOUT_PROTOCOL") || "https",
port: System.get_env("PORT"),
host: System.get_env("BLOCKSCOUT_HOST") || "localhost",
path: System.get_env("NETWORK_PATH") || "/"
path: System.get_env("NETWORK_PATH") || "/",
api_path: System.get_env("API_PATH") || "/"
]
config :block_scout_web, BlockScoutWeb.Tracer, env: "production", disabled?: true

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.API.RPC.StatsController do
use Explorer.Schema
alias Explorer.{Chain, ExchangeRates}
alias Explorer.Chain.Cache.AddressSum
alias Explorer.Chain.Cache.{AddressSum, AddressSumMinusBurnt}
alias Explorer.Chain.Wei
def tokensupply(conn, params) do
@ -41,6 +41,16 @@ defmodule BlockScoutWeb.API.RPC.StatsController do
render(conn, "ethsupply.json", total_supply: cached_wei_total_supply)
end
def coinsupply(conn, _params) do
cached_coin_total_supply_wei = AddressSumMinusBurnt.get_sum_minus_burnt()
cached_coin_total_supply =
%Wei{value: Decimal.new(cached_coin_total_supply_wei)}
|> Wei.to(:ether)
render(conn, "coinsupply.json", cached_coin_total_supply)
end
def ethprice(conn, _params) do
symbol = Application.get_env(:explorer, :coin)
rates = ExchangeRates.lookup(symbol)

@ -29,9 +29,9 @@ defmodule BlockScoutWeb.Endpoint do
browserconfig.xml
mstile-150x150.png
safari-pinned-tab.svg
site.manifest
robots.txt
)
),
only_matching: ~w(manifest)
)
# Code reloading can be explicitly enabled under the

@ -273,6 +273,8 @@ defmodule BlockScoutWeb.Etherscan do
"result" => "101959776311500000000000000"
}
@stats_coinsupply_example_value 101_959_776.3115
@stats_ethprice_example_value %{
"status" => "1",
"message" => "OK",
@ -589,6 +591,12 @@ defmodule BlockScoutWeb.Etherscan do
example: ~s("Some Token Name")
}
@token_id_type %{
type: "integer",
definition: "id of token",
example: ~s("0")
}
@token_symbol_type %{
type: "string",
definition: "Trading symbol of the token.",
@ -752,6 +760,7 @@ defmodule BlockScoutWeb.Etherscan do
example: ~s("663046792267785498951364")
},
tokenName: @token_name_type,
tokenID: @token_id_type,
tokenSymbol: @token_symbol_type,
tokenDecimal: @token_decimal_type,
transactionIndex: @transaction_index_type,
@ -1821,7 +1830,7 @@ defmodule BlockScoutWeb.Etherscan do
message: @message_type,
result: %{
type: "integer",
description: "The total supply.",
description: "The total supply in Wei from DB.",
example: ~s("101959776311500000000000000")
}
}
@ -1830,6 +1839,30 @@ defmodule BlockScoutWeb.Etherscan do
]
}
@stats_coinsupply_action %{
name: "coinsupply",
description: "Get total coin supply from DB minus burnt number.",
required_params: [],
optional_params: [],
responses: [
%{
code: "200",
description: "successful operation",
example_value: Jason.encode!(@stats_coinsupply_example_value),
model: %{
name: "Result",
fields: %{
result: %{
type: "integer",
description: "The total supply from DB minus burnt number in coin dimension.",
example: 101_959_776.3115
}
}
}
}
]
}
@stats_ethprice_action %{
name: "ethprice",
description: "Get latest price in USD and BTC.",
@ -2336,6 +2369,7 @@ defmodule BlockScoutWeb.Etherscan do
@stats_tokensupply_action,
@stats_ethsupplyexchange_action,
@stats_ethsupply_action,
@stats_coinsupply_action,
@stats_ethprice_action
]
}

@ -95,7 +95,7 @@ defmodule BlockScoutWeb.Notifier do
def handle_event({:chain_event, :internal_transactions, :realtime, internal_transactions}) do
internal_transactions
|> Stream.map(
&(InternalTransaction
&(InternalTransaction.where_nonpending_block()
|> Repo.get_by(transaction_hash: &1.transaction_hash, index: &1.index)
|> Repo.preload([:from_address, :to_address, transaction: :block]))
)

@ -54,10 +54,22 @@ defmodule BlockScoutWeb.Router do
get("/eth_rpc_api_docs", APIDocsController, :eth_rpc)
end
scope "/verify_smart_contract" do
pipe_through(:api)
url_params = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url]
api_path = url_params[:api_path]
path = url_params[:path]
post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create)
if path != api_path do
scope to_string(api_path) <> "/verify_smart_contract" do
pipe_through(:api)
post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create)
end
else
scope "/verify_smart_contract" do
pipe_through(:api)
post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create)
end
end
if Application.get_env(:block_scout_web, WebRouter)[:enabled] do

@ -3,7 +3,11 @@
<%= if @use_custom_tooltip == true do %>
<span><%= name %> (<%= short_hash(@address) %>...)</span>
<% else %>
<span data-toggle="tooltip" data-placement="top" title="<%= @address %>"><%= name %> (<%= short_hash(@address) %>...)</span>
<span data-toggle="tooltip" data-placement="top" title="<%= @address %>">
<span class="d-none d-md-none d-lg-inline d-xl-inline"><%= short_contract_name(name, 30) %></span>
<span class="d-inline d-md-inline d-lg-none d-xl-none"><%= short_contract_name(name, 10) %></span>
<span> (<%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %>)</span>
</span>
<% end %>
<% else %>
<%= if assigns[:truncate] do %>

@ -62,7 +62,9 @@
<h3 class="address-detail-hash-title <%= if BlockScoutWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address %></h3>
<div class="d-flex flex-column flex-lg-row justify-content-start text-muted">
<%= if address_name = primary_name(@address) do %>
<strong class="mr-4 mb-2 text-dark"><%= address_name %></strong>
<strong class="mr-4 mb-2 text-dark" title="<%= address_name %>">
<%= short_contract_name(address_name, 30) %>
</strong>
<% end %>
<%= if @address.token do %>
<span class="mr-4 mb-2">

@ -1,4 +1,6 @@
<section class="container" data-page="coin-balance-history">
<script defer src="<%= static_path(@conn, "/js/chart-loader.js") %>"></script>
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<section>

@ -35,6 +35,28 @@
</div>
</div>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, "Include nightly builds" %>
<div class="center-column">
<div class="form-radios-group">
<div class="radio-big">
<%= radio_button f, :nightly_builds, false, checked: true, class: "form-check-input nightly-builds-false" %>
<div class="radio-icon"></div>
<%= label :nightly_builds, :false, gettext("No"), class: "radio-text" %>
</div>
<div class="radio-big">
<%= radio_button f, :nightly_builds, true, class: "form-check-input nightly-builds-true", "aria-describedby": "nightly_builds-help-block" %>
<div class="radio-icon"></div>
<%= label :nightly_builds, :true, gettext("Yes"), class: "radio-text" %>
</div>
</div>
<%= error_tag f, :nightly_builds, id: "nightly_builds-help-block", class: "text-danger form-error" %>
</div>
<div class="smart-contract-form-group-tooltip">Select yes if you want to show nightly builds.</div>
</div>
</div>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :compiler_version, gettext("Compiler") %>
@ -62,12 +84,12 @@
<div class="center-column">
<div class="form-radios-group">
<div class="radio-big">
<%= radio_button f, :optimization, false, checked: true, class: "form-check-input" %>
<%= radio_button f, :optimization, false, class: "form-check-input optimization-false" %>
<div class="radio-icon"></div>
<%= label :smart_contract_optimization, :false, gettext("No"), class: "radio-text" %>
</div>
<div class="radio-big">
<%= radio_button f, :optimization, true, class: "form-check-input", "aria-describedby": "optimization-help-block" %>
<%= radio_button f, :optimization, true, checked: true, class: "form-check-input optimization-true", "aria-describedby": "optimization-help-block" %>
<div class="radio-icon"></div>
<%= label :smart_contract_optimization, :true, gettext("Yes"), class: "radio-text" %>
</div>
@ -78,7 +100,7 @@
</div>
</div>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group optimization-runs">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :name, gettext("Optimization runs") %>
<div class="center-column">
@ -110,12 +132,12 @@
<%= label :autodetect_contructor_args, :false, gettext("No"), class: "radio-text" %>
</div>
<div class="radio-big">
<%= radio_button f, :autodetect_contructor_args, true, class: "form-check-input autodetecttrue", "aria-describedby": "optimization-help-block" %>
<%= radio_button f, :autodetect_contructor_args, true, class: "form-check-input autodetecttrue", "aria-describedby": "autodetect_contructor_args-help-block" %>
<div class="radio-icon"></div>
<%= label :autodetect_contructor_args, :true, gettext("Yes"), class: "radio-text" %>
</div>
</div>
<%= error_tag f, :autodetect_contructor_args, id: "optimization-help-block", class: "text-danger form-error" %>
<%= error_tag f, :autodetect_contructor_args, id: "autodetect_contructor_args-help-block", class: "text-danger form-error" %>
</div>
</div>
</div>

@ -5,13 +5,6 @@
<div class="dashboard-banner-network-graph">
<!-- Graph -->
<div class="dashboard-banner-chart">
<div hidden data-chart-loading-message class="tile tile-muted text-center mt-5">
<span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading chart") %>...
</div>
<button data-chart-error-message class="alert alert-danger col-12 text-left mt-5" style="display: none;">
<span><%= gettext("There was a problem loading the chart.") %></span>
</button>
@ -79,6 +72,7 @@
</div>
</div>
</div>
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/chart-loader.js") %>"></script>
</div>
<section class="container">
<div class="card card-chain-blocks">

@ -1,5 +1,5 @@
<link rel="preload" href="<%= static_path(@conn, "/css/awesomplete.css") %>" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="<%= static_path(@conn, "/css/awesomplete.css") %>"></noscript>
<link rel="stylesheet" href="<%= static_path(@conn, "/css/awesomplete.css") %>">
<script src="<%= static_path(@conn, "/js/awesomplete.min.js") %>"></script>
<script src="<%= static_path(@conn, "/js/awesomplete-util.min.js") %>"></script>
<nav class="navbar navbar-dark navbar-expand-lg navbar-primary" data-selector="navbar" id="top-navbar">

@ -4,14 +4,15 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
<link rel="preload" href="<%= static_path(@conn, "/css/non-critical.css") %>" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="<%= static_path(@conn, "/css/non-critical.css") %>"></noscript>
<link rel="stylesheet" href="<%= static_path(@conn, "/css/non-critical.css") %>">
<link rel="apple-touch-icon" sizes="180x180" href="<%= static_path(@conn, "/apple-touch-icon.png") %>">
<link rel="icon" type="image/png" sizes="32x32" href="<%= static_path(@conn, "/images/favicon-32x32.png") %>">
<link rel="icon" type="image/png" sizes="16x16" href="<%= static_path(@conn, "/images/favicon-16x16.png") %>">
<link rel="manifest" href="<%= static_path(@conn, "/site.webmanifest") %>">
<link rel="manifest" href="<%= static_path(@conn, "/manifest.webmanifest") %>">
<link rel="mask-icon" href="<%= static_path(@conn, "/safari-pinned-tab.svg") %>" color="#5bbad5">
<link rel="shortcut icon" type='image/x-icon' href="<%= static_path(@conn, "/images/favicon.ico") %>">
<meta name="msapplication-TileColor" content="#7dd79f">
@ -19,6 +20,18 @@
<meta name="theme-color" content="#ffffff">
<%= render_existing(@view_module, "_metatags.html", assigns) || render("_default_title.html") %>
<script defer data-cfasync="false">
window.localized = {
'Blocks Indexed': '<%= gettext("Blocks Indexed") %>',
'Block Processing': '<%= gettext("Block Mined, awaiting import...") %>',
'Indexing Tokens': '<%= gettext("Indexing Tokens") %>',
'Less than': '<%= gettext("Less than") %>',
'Market Cap': '<%= gettext("Market Cap") %>',
'Price': '<%= gettext("Price") %>',
'Ether': '<%= gettext("Ether") %>'
}
</script>
</head>
<body>
@ -51,18 +64,7 @@
</main>
<%= render BlockScoutWeb.LayoutView, "_footer.html", assigns %>
</div>
<script>
window.localized = {
'Blocks Indexed': '<%= gettext("Blocks Indexed") %>',
'Block Processing': '<%= gettext("Block Mined, awaiting import...") %>',
'Indexing Tokens': '<%= gettext("Indexing Tokens") %>',
'Less than': '<%= gettext("Less than") %>',
'Market Cap': '<%= gettext("Market Cap") %>',
'Price': '<%= gettext("Price") %>',
'Ether': '<%= gettext("Ether") %>'
}
</script>
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/app.js") %>"></script>
<%= render_existing(@view_module, "scripts.html", assigns) %>
</body>
</html>

@ -144,6 +144,7 @@
data-clipboard-text="<%= @transaction.input %>"
data-placement="top"
data-toggle="tooltip"
title='<%= gettext("Copy Txn Input") %>'
style="display: none;"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="32" height="32">

@ -323,6 +323,14 @@ defmodule BlockScoutWeb.AddressView do
"0x" <> short_address
end
def short_contract_name(name, max_length) do
part_length = Kernel.trunc(max_length / 4)
if String.length(name) <= max_length,
do: name,
else: "#{String.slice(name, 0, max_length - part_length)}..#{String.slice(name, -part_length, part_length)}"
end
def address_page_title(address) do
cond do
smart_contract_verified?(address) -> "#{address.smart_contract.name} (#{to_string(address)})"

@ -121,7 +121,7 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
}
end
defp prepare_token_transfer(token_transfer) do
defp prepare_common_token_transfer(token_transfer) do
%{
"blockNumber" => to_string(token_transfer.block_number),
"timeStamp" => to_string(DateTime.to_unix(token_transfer.block_timestamp)),
@ -132,7 +132,6 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
"contractAddress" => to_string(token_transfer.token_contract_address_hash),
"to" => to_string(token_transfer.to_address_hash),
"logIndex" => to_string(token_transfer.token_log_index),
"value" => get_token_value(token_transfer),
"tokenName" => token_transfer.token_name,
"tokenSymbol" => token_transfer.token_symbol,
"tokenDecimal" => to_string(token_transfer.token_decimals),
@ -146,12 +145,20 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
}
end
defp get_token_value(%{token_type: "ERC-721"} = token_transfer) do
to_string(token_transfer.token_id)
defp prepare_token_transfer(%{token_type: "ERC-721"} = token_transfer) do
token_transfer
|> prepare_common_token_transfer()
|> Map.put_new(:tokenID, token_transfer.token_id)
end
defp prepare_token_transfer(%{token_type: "ERC-20"} = token_transfer) do
token_transfer
|> prepare_common_token_transfer()
|> Map.put_new(:value, to_string(token_transfer.amount))
end
defp get_token_value(token_transfer) do
to_string(token_transfer.amount)
defp prepare_token_transfer(token_transfer) do
prepare_common_token_transfer(token_transfer)
end
defp prepare_block(block) do

@ -4,6 +4,8 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
alias BlockScoutWeb.API.RPC.RPCView
alias Explorer.Chain.{Address, DecompiledSmartContract, SmartContract}
defguardp is_empty_string(input) when input == "" or input == nil
def render("listcontracts.json", %{contracts: contracts}) do
contracts = Enum.map(contracts, &prepare_contract/1)
@ -35,7 +37,11 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
"CompilerVersion" => "",
"DecompiledSourceCode" => "",
"DecompilerVersion" => decompiler_version(nil),
"OptimizationUsed" => ""
"OptimizationUsed" => "",
"OptimizationRuns" => "",
"EVMVersion" => "",
"ConstructorArguments" => "",
"ExternalLibraries" => ""
}
end
@ -43,6 +49,68 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
decompiled_smart_contract = latest_decompiled_smart_contract(address.decompiled_smart_contracts)
contract = address.smart_contract || %{}
optimization = Map.get(contract, :optimization, "")
contract_output = %{
"Address" => to_string(address.hash)
}
contract_output
|> set_decompiled_contract_data(decompiled_smart_contract)
|> set_optimization_runs(contract, optimization)
|> set_constructor_arguments(contract)
|> set_external_libraries(contract)
|> set_verified_contract_data(contract, address, optimization)
end
defp set_decompiled_contract_data(contract_output, decompiled_smart_contract) do
if decompiled_smart_contract do
contract_output
|> Map.put_new(:DecompiledSourceCode, decompiled_source_code(decompiled_smart_contract))
|> Map.put_new(:DecompilerVersion, decompiler_version(decompiled_smart_contract))
else
contract_output
end
end
defp set_optimization_runs(contract_output, contract, optimization) do
optimization_runs = Map.get(contract, :optimization_runs, "")
if optimization && optimization != "" do
contract_output
|> Map.put_new(:OptimizationRuns, optimization_runs)
else
contract_output
end
end
defp set_constructor_arguments(contract_output, %{constructor_arguments: arguments}) when is_empty_string(arguments),
do: contract_output
defp set_constructor_arguments(contract_output, %{constructor_arguments: arguments}) do
contract_output
|> Map.put_new(:ConstructorArguments, arguments)
end
defp set_constructor_arguments(contract_output, _), do: contract_output
defp set_external_libraries(contract_output, contract) do
external_libraries = Map.get(contract, :external_libraries, [])
if Enum.count(external_libraries) > 0 do
external_libraries_without_id =
Enum.map(external_libraries, fn %{name: name, address_hash: address_hash} ->
%{"name" => name, "address_hash" => address_hash}
end)
contract_output
|> Map.put_new(:ExternalLibraries, external_libraries_without_id)
else
contract_output
end
end
defp set_verified_contract_data(contract_output, contract, address, optimization) do
contract_abi =
if is_nil(address.smart_contract) do
"Contract source code not verified"
@ -51,27 +119,28 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
end
contract_optimization =
case Map.get(contract, :optimization, "") do
case optimization do
true ->
"1"
"true"
false ->
"0"
"false"
"" ->
""
end
%{
"Address" => to_string(address.hash),
"SourceCode" => Map.get(contract, :contract_source_code, ""),
"ABI" => contract_abi,
"ContractName" => Map.get(contract, :name, ""),
"DecompiledSourceCode" => decompiled_source_code(decompiled_smart_contract),
"DecompilerVersion" => decompiler_version(decompiled_smart_contract),
"CompilerVersion" => Map.get(contract, :compiler_version, ""),
"OptimizationUsed" => contract_optimization
}
if Map.equal?(contract, %{}) do
contract_output
else
contract_output
|> Map.put_new(:SourceCode, Map.get(contract, :contract_source_code, ""))
|> Map.put_new(:ABI, contract_abi)
|> Map.put_new(:ContractName, Map.get(contract, :name, ""))
|> Map.put_new(:CompilerVersion, Map.get(contract, :compiler_version, ""))
|> Map.put_new(:OptimizationUsed, contract_optimization)
|> Map.put_new(:EVMVersion, Map.get(contract, :evm_version, ""))
end
end
defp prepare_contract(%Address{
@ -80,10 +149,7 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
}) do
%{
"Address" => to_string(hash),
"ABI" => "Contract source code not verified",
"ContractName" => "",
"CompilerVersion" => "",
"OptimizationUsed" => ""
"ABI" => "Contract source code not verified"
}
end

@ -9,6 +9,15 @@ defmodule BlockScoutWeb.API.RPC.RPCView do
}
end
def render("show_value.json", data) do
{value, _} =
data
|> Decimal.to_string(:normal)
|> Float.parse()
value
end
def render("error.json", %{error: message} = assigns) do
%{
"status" => "0",

@ -15,6 +15,10 @@ defmodule BlockScoutWeb.API.RPC.StatsView do
RPCView.render("show.json", data: total_supply)
end
def render("coinsupply.json", total_supply) do
RPCView.render("show_value.json", total_supply)
end
def render("ethprice.json", %{rates: rates}) do
RPCView.render("show.json", data: prepare_rates(rates))
end

@ -34,10 +34,17 @@ defmodule BlockScoutWeb.APIDocsView do
end)
end
def blockscout_url do
def blockscout_url(is_api) do
url_params = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url]
host = url_params[:host]
path = url_params[:path]
path =
if is_api do
url_params[:api_path]
else
url_params[:path]
end
scheme = Keyword.get(url_params, :scheme, "http")
if host != "localhost" do
@ -49,12 +56,18 @@ defmodule BlockScoutWeb.APIDocsView do
end
def api_url do
blockscout_url()
is_api = true
is_api
|> blockscout_url()
|> Path.join("api")
end
def eth_rpc_api_url do
blockscout_url()
is_api = true
is_api
|> blockscout_url()
|> Path.join("api/eth_rpc")
end
end

@ -10,7 +10,7 @@ defmodule BlockScoutWeb.SmartContractView do
def named_argument?(%{"name" => _}), do: true
def named_argument?(_), do: false
def values(addresses, type) when type == "address[]" do
def values(addresses, type) when is_list(addresses) and type == "address[]" do
addresses
|> Enum.map(&values(&1, "address"))
|> Enum.join(", ")

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do
alias BlockScoutWeb.CurrencyHelpers
alias Explorer.Chain.{Address, SmartContract, Token}
import BlockScoutWeb.APIDocsView, only: [blockscout_url: 0]
import BlockScoutWeb.APIDocsView, only: [blockscout_url: 1]
@tabs ["token_transfers", "metadata"]
@ -55,7 +55,8 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do
def qr_code(conn, token_id, hash) do
token_instance_path = token_instance_path(conn, :show, to_string(hash), to_string(token_id))
url = Path.join(blockscout_url(), token_instance_path)
is_api = false
url = Path.join(blockscout_url(is_api), token_instance_path)
url
|> QRCode.to_png()

@ -60,13 +60,13 @@ defmodule BlockScoutWeb.Mixfile do
defp deps do
[
# GraphQL toolkit
{:absinthe, "~> 1.4"},
{:absinthe, "~> 1.5.0-rc.2"},
# Integrates Absinthe subscriptions with Phoenix
{:absinthe_phoenix, git: "https://github.com/ayrat555/absinthe_phoenix.git", branch: "ab-update-plug"},
{:absinthe_phoenix, "~> 1.5.0-rc.0"},
# Plug support for Absinthe
{:absinthe_plug, git: "https://github.com/ayrat555/absinthe_plug.git", branch: "ab-enable-default-query"},
{:absinthe_plug, "~> 1.5.0-rc.1"},
# Absinthe support for the Relay framework
{:absinthe_relay, "~> 1.4"},
{:absinthe_relay, "~> 1.5.0-rc.0"},
{:bypass, "~> 1.0", only: :test},
# To add (CORS)(https://www.w3.org/TR/cors/)
{:cors_plug, "~> 2.0"},
@ -76,7 +76,7 @@ defmodule BlockScoutWeb.Mixfile do
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
# Need until https://github.com/absinthe-graphql/absinthe_relay/pull/125 is released, then can be removed
# The current `absinthe_relay` is compatible though as shown from that PR
{:ecto, "~> 3.0", override: true},
{:ecto, "~> 3.3", override: true},
{:ex_cldr, "~> 2.7"},
{:ex_cldr_numbers, "~> 2.6"},
{:ex_cldr_units, "~> 2.5"},

@ -13,7 +13,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:193
#: lib/block_scout_web/templates/transaction/overview.html.eex:194
msgid " Token Transfer"
msgstr ""
@ -72,7 +72,7 @@ msgid "(query)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:42
#: lib/block_scout_web/templates/layout/app.html.eex:55
msgid "- We're indexing this chain right now. Some of the counts may be inaccurate."
msgstr ""
@ -87,7 +87,7 @@ msgid "A string with the name of the module to be invoked."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:125
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:147
msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr ""
@ -150,7 +150,7 @@ msgid "Anything not in this list is not supported. Click on the method to be tak
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:47
#: lib/block_scout_web/templates/chain/show.html.eex:40
msgid "Average block time"
msgstr ""
@ -165,7 +165,7 @@ msgid "Balance"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:14
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:16
msgid "Balances"
msgstr ""
@ -208,7 +208,7 @@ msgid "Block Height: %{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:57
#: lib/block_scout_web/templates/layout/app.html.eex:27
msgid "Block Mined, awaiting import..."
msgstr ""
@ -233,14 +233,14 @@ msgid "BlockScout provides analytics data, API, and Smart Contract tools for the
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/chain/show.html.eex:81
#: lib/block_scout_web/templates/layout/_topnav.html.eex:31
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
msgid "Blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:56
#: lib/block_scout_web/templates/layout/app.html.eex:26
msgid "Blocks Indexed"
msgstr ""
@ -266,7 +266,7 @@ msgid "Call Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:274
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:296
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
@ -289,7 +289,7 @@ msgid "Clear"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:62
msgid "Compiler"
msgstr ""
@ -304,7 +304,7 @@ msgid "Connection Lost"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:10
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:12
#: lib/block_scout_web/templates/block/index.html.eex:6
msgid "Connection Lost, click to load newer blocks"
msgstr ""
@ -370,7 +370,7 @@ msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:139
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:161
msgid "Contract Libraries"
msgstr ""
@ -467,7 +467,7 @@ msgid "Create2"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:94
#: lib/block_scout_web/templates/address/overview.html.eex:96
msgid "Created by"
msgstr ""
@ -579,7 +579,7 @@ 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:51
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:73
msgid "EVM Version"
msgstr ""
@ -599,7 +599,7 @@ msgid "Emission Reward"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:93
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:115
msgid "Enter the Solidity Contract Code"
msgstr ""
@ -631,7 +631,7 @@ msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:107
#: lib/block_scout_web/templates/address/overview.html.eex:109
msgid "Error: Could not determine contract creator."
msgstr ""
@ -643,11 +643,11 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:15
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:20
#: lib/block_scout_web/templates/layout/app.html.eex:62
#: lib/block_scout_web/templates/layout/app.html.eex:32
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/_tile.html.eex:29
#: lib/block_scout_web/templates/transaction/overview.html.eex:180
#: lib/block_scout_web/templates/transaction/overview.html.eex:215
#: lib/block_scout_web/templates/transaction/overview.html.eex:181
#: lib/block_scout_web/templates/transaction/overview.html.eex:216
#: lib/block_scout_web/views/wei_helpers.ex:78
msgid "Ether"
msgstr ""
@ -898,7 +898,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/address_view.ex:308
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:71
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:72
#: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:314
msgid "Token Transfers"
@ -961,30 +961,30 @@ msgid "It could still be in the TX Pool of a different node, waiting to be broad
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:79
#: lib/block_scout_web/templates/address/overview.html.eex:81
msgid "Last Balance Update: Block #"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:59
#: lib/block_scout_web/templates/layout/app.html.eex:29
msgid "Less than"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:154
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:176
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:198
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:220
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:242
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:264
msgid "Library Address"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:144
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:166
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:188
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:210
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:232
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:254
msgid "Library Name"
msgstr ""
@ -999,7 +999,7 @@ msgid "License ID"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:243
#: lib/block_scout_web/templates/transaction/overview.html.eex:244
msgid "Limit"
msgstr ""
@ -1010,7 +1010,7 @@ msgid "Loading..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:268
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:290
msgid "Loading...."
msgstr ""
@ -1033,8 +1033,8 @@ msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:31
#: lib/block_scout_web/templates/layout/app.html.eex:60
#: lib/block_scout_web/templates/chain/show.html.eex:24
#: lib/block_scout_web/templates/layout/app.html.eex:30
#: lib/block_scout_web/views/address_view.ex:126
#: lib/block_scout_web/views/address_view.ex:126
msgid "Market Cap"
@ -1067,7 +1067,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:148
#: lib/block_scout_web/templates/chain/show.html.eex:142
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:10
#: lib/block_scout_web/templates/transaction/index.html.eex:10
msgid "More transactions have come in"
@ -1096,8 +1096,9 @@ msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:67
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:110
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:46
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:89
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:132
msgid "No"
msgstr ""
@ -1114,7 +1115,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:39
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:83
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:105
msgid "Optimization runs"
msgstr ""
@ -1169,14 +1170,14 @@ msgid "Potential matches from our contract method database:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:24
#: lib/block_scout_web/templates/layout/app.html.eex:61
#: lib/block_scout_web/templates/chain/show.html.eex:17
#: lib/block_scout_web/templates/layout/app.html.eex:31
msgid "Price"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:33
#: lib/block_scout_web/templates/address/overview.html.eex:131
#: lib/block_scout_web/templates/address/overview.html.eex:133
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:51
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:93
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36
@ -1218,7 +1219,7 @@ msgid "Request URL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:271
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:293
msgid "Reset"
msgstr ""
@ -1305,7 +1306,7 @@ msgid "Block Rewards"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:34
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:36
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:61
#: lib/block_scout_web/templates/address_logs/index.html.eex:21
#: lib/block_scout_web/templates/address_token/index.html.eex:13
@ -1313,7 +1314,7 @@ msgstr ""
#: 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
#: lib/block_scout_web/templates/chain/show.html.eex:85
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:19
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:20
#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:21
@ -1327,7 +1328,7 @@ msgid "Something went wrong, click to reload."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:154
#: lib/block_scout_web/templates/chain/show.html.eex:148
msgid "Something went wrong, click to retry."
msgstr ""
@ -1392,7 +1393,7 @@ msgid "There are no transactions."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:39
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:41
msgid "There is no coin history for this address."
msgstr ""
@ -1402,8 +1403,8 @@ msgid "There is no decompilded contracts for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:25
#: lib/block_scout_web/templates/chain/show.html.eex:16
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:27
#: lib/block_scout_web/templates/chain/show.html.eex:9
msgid "There was a problem loading the chart."
msgstr ""
@ -1456,7 +1457,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:64
#: lib/block_scout_web/templates/chain/show.html.eex:57
msgid "Total blocks"
msgstr ""
@ -1538,7 +1539,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:237
#: lib/block_scout_web/templates/transaction/overview.html.eex:238
msgid "Used"
msgstr ""
@ -1568,8 +1569,8 @@ msgid "Validator Info"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:180
#: lib/block_scout_web/templates/transaction/overview.html.eex:215
#: lib/block_scout_web/templates/transaction/overview.html.eex:181
#: lib/block_scout_web/templates/transaction/overview.html.eex:216
msgid "Value"
msgstr ""
@ -1580,7 +1581,7 @@ msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:270
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:292
msgid "Verify & publish"
msgstr ""
@ -1597,12 +1598,12 @@ msgid "Version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:86
#: lib/block_scout_web/templates/chain/show.html.eex:80
msgid "View All Blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:144
#: lib/block_scout_web/templates/chain/show.html.eex:138
msgid "View All Transactions"
msgstr ""
@ -1644,7 +1645,7 @@ msgid "WEI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:72
#: lib/block_scout_web/templates/chain/show.html.eex:65
msgid "Wallet addresses"
msgstr ""
@ -1654,13 +1655,14 @@ msgid "Wei"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:72
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:115
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:51
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:94
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:137
msgid "Yes"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:99
#: lib/block_scout_web/templates/address/overview.html.eex:101
msgid "at"
msgstr ""
@ -1718,8 +1720,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37
#: lib/block_scout_web/templates/address/overview.html.eex:132
#: lib/block_scout_web/templates/address/overview.html.eex:140
#: lib/block_scout_web/templates/address/overview.html.eex:134
#: lib/block_scout_web/templates/address/overview.html.eex:142
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:94
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:102
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:106
@ -1745,7 +1747,7 @@ msgid "Decimals"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:233
#: lib/block_scout_web/templates/transaction/overview.html.eex:234
msgid "Gas"
msgstr ""
@ -1755,13 +1757,12 @@ msgid "If it still does not show up after 1 hour, please check with your sender/
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:58
#: lib/block_scout_web/templates/layout/app.html.eex:28
msgid "Indexing Tokens"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22
#: lib/block_scout_web/templates/chain/show.html.eex:13
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:24
msgid "Loading chart"
msgstr ""
@ -1773,7 +1774,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18
#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:72
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:73
msgid "Metadata"
msgstr ""
@ -1783,7 +1784,7 @@ msgid "Module"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:56
#: lib/block_scout_web/templates/chain/show.html.eex:49
msgid "Total transactions"
msgstr ""
@ -1794,6 +1795,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:134
#: lib/block_scout_web/templates/transaction/overview.html.eex:147
msgid "Copy Txn Input"
msgstr ""
@ -1867,7 +1869,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_transaction/index.html.eex:15
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10
#: lib/block_scout_web/templates/block_transaction/index.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:145
#: lib/block_scout_web/templates/chain/show.html.eex:139
#: lib/block_scout_web/templates/layout/_topnav.html.eex:50
#: lib/block_scout_web/views/address_view.ex:306
msgid "Transactions"

@ -13,7 +13,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:193
#: lib/block_scout_web/templates/transaction/overview.html.eex:194
msgid " Token Transfer"
msgstr ""
@ -72,7 +72,7 @@ msgid "(query)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:42
#: lib/block_scout_web/templates/layout/app.html.eex:55
msgid "- We're indexing this chain right now. Some of the counts may be inaccurate."
msgstr ""
@ -87,7 +87,7 @@ msgid "A string with the name of the module to be invoked."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:125
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:147
msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr ""
@ -150,7 +150,7 @@ msgid "Anything not in this list is not supported. Click on the method to be tak
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:47
#: lib/block_scout_web/templates/chain/show.html.eex:40
msgid "Average block time"
msgstr ""
@ -165,7 +165,7 @@ msgid "Balance"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:14
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:16
msgid "Balances"
msgstr ""
@ -208,7 +208,7 @@ msgid "Block Height: %{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:57
#: lib/block_scout_web/templates/layout/app.html.eex:27
msgid "Block Mined, awaiting import..."
msgstr ""
@ -233,14 +233,14 @@ msgid "BlockScout provides analytics data, API, and Smart Contract tools for the
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/chain/show.html.eex:81
#: lib/block_scout_web/templates/layout/_topnav.html.eex:31
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
msgid "Blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:56
#: lib/block_scout_web/templates/layout/app.html.eex:26
msgid "Blocks Indexed"
msgstr ""
@ -266,7 +266,7 @@ msgid "Call Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:274
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:296
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
@ -289,7 +289,7 @@ msgid "Clear"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:62
msgid "Compiler"
msgstr ""
@ -304,7 +304,7 @@ msgid "Connection Lost"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:10
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:12
#: lib/block_scout_web/templates/block/index.html.eex:6
msgid "Connection Lost, click to load newer blocks"
msgstr ""
@ -370,7 +370,7 @@ msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:139
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:161
msgid "Contract Libraries"
msgstr ""
@ -467,7 +467,7 @@ msgid "Create2"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:94
#: lib/block_scout_web/templates/address/overview.html.eex:96
msgid "Created by"
msgstr ""
@ -579,7 +579,7 @@ 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:51
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:73
msgid "EVM Version"
msgstr ""
@ -599,7 +599,7 @@ msgid "Emission Reward"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:93
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:115
msgid "Enter the Solidity Contract Code"
msgstr ""
@ -631,7 +631,7 @@ msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:107
#: lib/block_scout_web/templates/address/overview.html.eex:109
msgid "Error: Could not determine contract creator."
msgstr ""
@ -643,11 +643,11 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:15
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:20
#: lib/block_scout_web/templates/layout/app.html.eex:62
#: lib/block_scout_web/templates/layout/app.html.eex:32
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/_tile.html.eex:29
#: lib/block_scout_web/templates/transaction/overview.html.eex:180
#: lib/block_scout_web/templates/transaction/overview.html.eex:215
#: lib/block_scout_web/templates/transaction/overview.html.eex:181
#: lib/block_scout_web/templates/transaction/overview.html.eex:216
#: lib/block_scout_web/views/wei_helpers.ex:78
msgid "Ether"
msgstr ""
@ -898,7 +898,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/address_view.ex:308
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:71
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:72
#: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:314
msgid "Token Transfers"
@ -961,30 +961,30 @@ msgid "It could still be in the TX Pool of a different node, waiting to be broad
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:79
#: lib/block_scout_web/templates/address/overview.html.eex:81
msgid "Last Balance Update: Block #"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:59
#: lib/block_scout_web/templates/layout/app.html.eex:29
msgid "Less than"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:154
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:176
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:198
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:220
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:242
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:264
msgid "Library Address"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:144
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:166
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:188
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:210
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:232
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:254
msgid "Library Name"
msgstr ""
@ -999,7 +999,7 @@ msgid "License ID"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:243
#: lib/block_scout_web/templates/transaction/overview.html.eex:244
msgid "Limit"
msgstr ""
@ -1010,7 +1010,7 @@ msgid "Loading..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:268
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:290
msgid "Loading...."
msgstr ""
@ -1033,8 +1033,8 @@ msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:31
#: lib/block_scout_web/templates/layout/app.html.eex:60
#: lib/block_scout_web/templates/chain/show.html.eex:24
#: lib/block_scout_web/templates/layout/app.html.eex:30
#: lib/block_scout_web/views/address_view.ex:126
#: lib/block_scout_web/views/address_view.ex:126
msgid "Market Cap"
@ -1067,7 +1067,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:148
#: lib/block_scout_web/templates/chain/show.html.eex:142
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:10
#: lib/block_scout_web/templates/transaction/index.html.eex:10
msgid "More transactions have come in"
@ -1096,8 +1096,9 @@ msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:67
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:110
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:46
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:89
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:132
msgid "No"
msgstr ""
@ -1114,7 +1115,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:39
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:83
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:105
msgid "Optimization runs"
msgstr ""
@ -1169,14 +1170,14 @@ msgid "Potential matches from our contract method database:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:24
#: lib/block_scout_web/templates/layout/app.html.eex:61
#: lib/block_scout_web/templates/chain/show.html.eex:17
#: lib/block_scout_web/templates/layout/app.html.eex:31
msgid "Price"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:33
#: lib/block_scout_web/templates/address/overview.html.eex:131
#: lib/block_scout_web/templates/address/overview.html.eex:133
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:51
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:93
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36
@ -1218,7 +1219,7 @@ msgid "Request URL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:271
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:293
msgid "Reset"
msgstr ""
@ -1305,7 +1306,7 @@ msgid "Block Rewards"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:34
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:36
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:61
#: lib/block_scout_web/templates/address_logs/index.html.eex:21
#: lib/block_scout_web/templates/address_token/index.html.eex:13
@ -1313,7 +1314,7 @@ msgstr ""
#: 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
#: lib/block_scout_web/templates/chain/show.html.eex:85
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:19
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:20
#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:21
@ -1327,7 +1328,7 @@ msgid "Something went wrong, click to reload."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:154
#: lib/block_scout_web/templates/chain/show.html.eex:148
msgid "Something went wrong, click to retry."
msgstr ""
@ -1392,7 +1393,7 @@ msgid "There are no transactions."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:39
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:41
msgid "There is no coin history for this address."
msgstr ""
@ -1402,8 +1403,8 @@ msgid "There is no decompilded contracts for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:25
#: lib/block_scout_web/templates/chain/show.html.eex:16
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:27
#: lib/block_scout_web/templates/chain/show.html.eex:9
msgid "There was a problem loading the chart."
msgstr ""
@ -1456,7 +1457,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:64
#: lib/block_scout_web/templates/chain/show.html.eex:57
msgid "Total blocks"
msgstr ""
@ -1538,7 +1539,7 @@ msgid "Use the search box to find a hosted network, or select from the list of a
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:237
#: lib/block_scout_web/templates/transaction/overview.html.eex:238
msgid "Used"
msgstr ""
@ -1568,8 +1569,8 @@ msgid "Validator Info"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:180
#: lib/block_scout_web/templates/transaction/overview.html.eex:215
#: lib/block_scout_web/templates/transaction/overview.html.eex:181
#: lib/block_scout_web/templates/transaction/overview.html.eex:216
msgid "Value"
msgstr ""
@ -1580,7 +1581,7 @@ msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:270
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:292
msgid "Verify & publish"
msgstr ""
@ -1597,12 +1598,12 @@ msgid "Version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:86
#: lib/block_scout_web/templates/chain/show.html.eex:80
msgid "View All Blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:144
#: lib/block_scout_web/templates/chain/show.html.eex:138
msgid "View All Transactions"
msgstr ""
@ -1644,7 +1645,7 @@ msgid "WEI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:72
#: lib/block_scout_web/templates/chain/show.html.eex:65
msgid "Wallet addresses"
msgstr ""
@ -1654,13 +1655,14 @@ msgid "Wei"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:72
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:115
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:51
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:94
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:137
msgid "Yes"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:99
#: lib/block_scout_web/templates/address/overview.html.eex:101
msgid "at"
msgstr ""
@ -1718,8 +1720,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37
#: lib/block_scout_web/templates/address/overview.html.eex:132
#: lib/block_scout_web/templates/address/overview.html.eex:140
#: lib/block_scout_web/templates/address/overview.html.eex:134
#: lib/block_scout_web/templates/address/overview.html.eex:142
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:94
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:102
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:106
@ -1745,7 +1747,7 @@ msgid "Decimals"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:233
#: lib/block_scout_web/templates/transaction/overview.html.eex:234
msgid "Gas"
msgstr ""
@ -1755,13 +1757,12 @@ msgid "If it still does not show up after 1 hour, please check with your sender/
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:58
#: lib/block_scout_web/templates/layout/app.html.eex:28
msgid "Indexing Tokens"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22
#: lib/block_scout_web/templates/chain/show.html.eex:13
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:24
msgid "Loading chart"
msgstr ""
@ -1773,7 +1774,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18
#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:72
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:73
msgid "Metadata"
msgstr ""
@ -1783,7 +1784,7 @@ msgid "Module"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:56
#: lib/block_scout_web/templates/chain/show.html.eex:49
msgid "Total transactions"
msgstr ""
@ -1794,6 +1795,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:134
#: lib/block_scout_web/templates/transaction/overview.html.eex:147
msgid "Copy Txn Input"
msgstr ""
@ -1867,7 +1869,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_transaction/index.html.eex:15
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10
#: lib/block_scout_web/templates/block_transaction/index.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:145
#: lib/block_scout_web/templates/chain/show.html.eex:139
#: lib/block_scout_web/templates/layout/_topnav.html.eex:50
#: lib/block_scout_web/views/address_view.ex:306
msgid "Transactions"

@ -134,7 +134,15 @@ defmodule BlockScoutWeb.AddressChannelTest do
|> insert(from_address: address)
|> with_block()
internal_transaction = insert(:internal_transaction, transaction: transaction, from_address: address, index: 0)
internal_transaction =
insert(
:internal_transaction,
transaction: transaction,
from_address: address,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
Notifier.handle_event({:chain_event, :internal_transactions, :realtime, [internal_transaction]})
@ -158,7 +166,14 @@ defmodule BlockScoutWeb.AddressChannelTest do
|> insert(to_address: address)
|> with_block()
internal_transaction = insert(:internal_transaction, transaction: transaction, to_address: address, index: 0)
internal_transaction =
insert(:internal_transaction,
transaction: transaction,
to_address: address,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
Notifier.handle_event({:chain_event, :internal_transactions, :realtime, [internal_transaction]})
@ -186,7 +201,14 @@ defmodule BlockScoutWeb.AddressChannelTest do
|> with_block()
internal_transaction =
insert(:internal_transaction, transaction: transaction, from_address: address, to_address: address, index: 0)
insert(:internal_transaction,
transaction: transaction,
from_address: address,
to_address: address,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
Notifier.handle_event({:chain_event, :internal_transactions, :realtime, [internal_transaction]})

@ -35,13 +35,15 @@ defmodule BlockScoutWeb.AddressContractControllerTest do
test "successfully renders the page when the address is a contract", %{conn: conn} do
address = insert(:address, contract_code: Factory.data("contract_code"), smart_contract: nil)
transaction = insert(:transaction, from_address: address)
transaction = insert(:transaction, from_address: address) |> with_block()
insert(
:internal_transaction_create,
index: 0,
transaction: transaction,
created_contract_address: address
created_contract_address: address,
block_hash: transaction.block_hash,
block_index: 0
)
conn = get(conn, address_contract_path(BlockScoutWeb.Endpoint, :index, address))

@ -44,7 +44,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: 1,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 1
)
to_internal_transaction =
@ -53,7 +55,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
to_address: address,
index: 2,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 2
)
path = address_internal_transaction_path(conn, :index, address, %{"type" => "JSON"})
@ -83,7 +87,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: 1,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 1
)
to_internal_transaction =
@ -92,7 +98,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
to_address: address,
index: 2,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 2
)
path = address_internal_transaction_path(conn, :index, address, %{"filter" => "from", "type" => "JSON"})
@ -125,7 +133,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: 1,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 1
)
to_internal_transaction =
@ -134,7 +144,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
to_address: address,
index: 2,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 2
)
path = address_internal_transaction_path(conn, :index, address, %{"filter" => "to", "type" => "JSON"})
@ -167,7 +179,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: 1,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 1
)
to_internal_transaction =
@ -177,7 +191,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
created_contract_address: address,
index: 2,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 2
)
path = address_internal_transaction_path(conn, :index, address, %{"filter" => "to", "type" => "JSON"})
@ -226,7 +242,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: index,
block_number: transaction_1.block_number,
transaction_index: transaction_1.index
transaction_index: transaction_1.index,
block_hash: a_block.hash,
block_index: index
)
end)
@ -239,7 +257,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: index,
block_number: transaction_2.block_number,
transaction_index: transaction_2.index
transaction_index: transaction_2.index,
block_hash: a_block.hash,
block_index: 20 + index
)
end)
@ -252,7 +272,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: index,
block_number: transaction_3.block_number,
transaction_index: transaction_3.index
transaction_index: transaction_3.index,
block_hash: b_block.hash,
block_index: index
)
end)
@ -265,7 +287,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: 11,
block_number: transaction_3.block_number,
transaction_index: transaction_3.index
transaction_index: transaction_3.index,
block_hash: b_block.hash,
block_index: 11
)
conn =
@ -304,7 +328,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
from_address: address,
index: index,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: index
)
end)
@ -335,7 +361,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
:internal_transaction,
transaction: transaction,
from_address: address,
index: index
index: index,
block_hash: transaction.block_hash,
block_index: index
)
end)

@ -21,13 +21,15 @@ defmodule BlockScoutWeb.AddressReadContractControllerTest do
test "successfully renders the page when the address is a contract", %{conn: conn} do
contract_address = insert(:contract_address)
transaction = insert(:transaction, from_address: contract_address)
transaction = insert(:transaction, from_address: contract_address) |> with_block()
insert(
:internal_transaction_create,
index: 0,
transaction: transaction,
created_contract_address: contract_address
created_contract_address: contract_address,
block_hash: transaction.block_hash,
block_index: 0
)
insert(:smart_contract, address_hash: contract_address.hash)
@ -42,13 +44,15 @@ defmodule BlockScoutWeb.AddressReadContractControllerTest do
test "returns not found for an unverified contract", %{conn: conn} do
contract_address = insert(:contract_address)
transaction = insert(:transaction, from_address: contract_address)
transaction = insert(:transaction, from_address: contract_address) |> with_block()
insert(
:internal_transaction_create,
index: 0,
transaction: transaction,
created_contract_address: contract_address
created_contract_address: contract_address,
block_hash: transaction.block_hash,
block_index: 0
)
conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, contract_address.hash))

@ -126,7 +126,9 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do
index: 0,
created_contract_address: address,
to_address: nil,
transaction: transaction
transaction: transaction,
block_hash: block.hash,
block_index: 0
)
conn = get(conn, address_transaction_path(conn, :index, address), %{"type" => "JSON"})

@ -88,21 +88,24 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
mining_address =
insert(:address,
fetched_coin_balance: 0,
fetched_coin_balance_block_number: 2,
fetched_coin_balance_block_number: 102,
inserted_at: Timex.shift(now, minutes: -10)
)
mining_address_hash = to_string(mining_address.hash)
# we space these very far apart so that we know it will consider the 0th block stale (it calculates how far
# back we'd need to go to get 24 hours in the past)
insert(:block, number: 0, timestamp: Timex.shift(now, hours: -50), miner: mining_address)
insert(:block, number: 1, timestamp: Timex.shift(now, hours: -25), miner: mining_address)
Enum.each(0..100, fn i ->
insert(:block, number: i, timestamp: Timex.shift(now, hours: -(102 - i) * 25), miner: mining_address)
end)
insert(:block, number: 101, timestamp: Timex.shift(now, hours: -25), miner: mining_address)
AverageBlockTime.refresh()
address =
insert(:address,
fetched_coin_balance: 100,
fetched_coin_balance_block_number: 0,
fetched_coin_balance_block_number: 100,
inserted_at: Timex.shift(now, minutes: -5)
)
@ -112,7 +115,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
%{
id: id,
method: "eth_getBalance",
params: [^address_hash, "0x1"]
params: [^address_hash, "0x65"]
}
],
_options ->
@ -148,7 +151,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert received_address.hash == address.hash
assert received_address.fetched_coin_balance == expected_wei
assert received_address.fetched_coin_balance_block_number == 1
assert received_address.fetched_coin_balance_block_number == 101
end
end
@ -1453,7 +1456,13 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
internal_transaction =
:internal_transaction_create
|> insert(transaction: transaction, index: 0, from_address: address)
|> insert(
transaction: transaction,
index: 0,
from_address: address,
block_hash: transaction.block_hash,
block_index: 0
)
|> with_contract_creation(contract_address)
params = %{
@ -1502,7 +1511,9 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
transaction: transaction,
index: 0,
type: :reward,
error: "some error"
error: "some error",
block_hash: transaction.block_hash,
block_index: 0
]
insert(:internal_transaction_create, internal_transaction_details)
@ -1532,7 +1543,12 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
|> with_block()
for index <- 0..2 do
insert(:internal_transaction_create, transaction: transaction, index: index)
insert(:internal_transaction_create,
transaction: transaction,
index: index,
block_hash: transaction.block_hash,
block_index: index
)
end
params = %{
@ -1606,7 +1622,14 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
internal_transaction =
:internal_transaction_create
|> insert(transaction: transaction, index: 0, from_address: address)
|> insert(
transaction: transaction,
index: 0,
from_address: address,
block_number: block.number,
block_hash: transaction.block_hash,
block_index: 0
)
|> with_contract_creation(contract_address)
params = %{
@ -1658,7 +1681,10 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
transaction: transaction,
index: 0,
type: :reward,
error: "some error"
error: "some error",
block_number: transaction.block_number,
block_hash: transaction.block_hash,
block_index: 0
]
insert(:internal_transaction_create, internal_transaction_details)
@ -1693,7 +1719,10 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
internal_transaction_details = %{
from_address: address,
transaction: transaction,
index: index
index: index,
block_number: transaction.block_number,
block_hash: transaction.block_hash,
block_index: index
}
insert(:internal_transaction_create, internal_transaction_details)
@ -1787,7 +1816,9 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
insert(:token_transfer, %{
token_contract_address: token_address,
token_id: 666,
transaction: transaction
transaction: transaction,
block: transaction.block,
block_number: transaction.block_number
})
{:ok, _} = Chain.token_from_address_hash(token_transfer.token_contract_address_hash)
@ -1804,7 +1835,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
|> get("/api", params)
|> json_response(200)
assert result["value"] == to_string(token_transfer.token_id)
assert result["tokenID"] == to_string(token_transfer.token_id)
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response)
@ -1817,7 +1848,9 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
|> insert()
|> with_block()
token_transfer = insert(:token_transfer, transaction: transaction)
token_transfer =
insert(:token_transfer, block: transaction.block, transaction: transaction, block_number: block.number)
{:ok, token} = Chain.token_from_address_hash(token_transfer.token_contract_address_hash)
params = %{
@ -1894,8 +1927,20 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
|> insert()
|> with_block()
insert(:token_transfer, from_address: address, transaction: transaction)
insert(:token_transfer, from_address: address, token_contract_address: contract_address, transaction: transaction)
insert(:token_transfer,
from_address: address,
transaction: transaction,
block: transaction.block,
block_number: transaction.block_number
)
insert(:token_transfer,
from_address: address,
token_contract_address: contract_address,
transaction: transaction,
block: transaction.block,
block_number: transaction.block_number
)
params = %{
"module" => "account",
@ -2615,6 +2660,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"logIndex" => %{"type" => "string"},
"value" => %{"type" => "string"},
"tokenName" => %{"type" => "string"},
"tokenID" => %{"type" => "string"},
"tokenSymbol" => %{"type" => "string"},
"tokenDecimal" => %{"type" => "string"},
"transactionIndex" => %{"type" => "string"},

@ -1,5 +1,6 @@
defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
use BlockScoutWeb.ConnCase
alias Explorer.Chain.SmartContract
alias Explorer.{Chain, Factory}
describe "listcontracts" do
@ -70,10 +71,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(address.hash)
}
]
@ -95,10 +93,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(address.hash)
}
]
@ -124,10 +119,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(address.hash)
}
]
@ -174,10 +166,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(decompiled_smart_contract.address_hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(decompiled_smart_contract.address_hash)
}
]
@ -199,10 +188,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(smart_contract.address_hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(smart_contract.address_hash)
}
]
@ -225,10 +211,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert %{
"ABI" => "Contract source code not verified",
"Address" => to_string(smart_contract.address_hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(smart_contract.address_hash)
} in response["result"]
refute to_string(non_match.address_hash) in Enum.map(response["result"], &Map.get(&1, "Address"))
@ -251,10 +234,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(contract_address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(contract_address.hash)
}
]
@ -281,10 +261,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["result"] == [
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(contract_address.hash),
"CompilerVersion" => "",
"ContractName" => "",
"OptimizationUsed" => ""
"Address" => to_string(contract_address.hash)
}
]
@ -423,7 +400,11 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
"CompilerVersion" => "",
"OptimizationUsed" => "",
"DecompiledSourceCode" => "",
"DecompilerVersion" => ""
"DecompilerVersion" => "",
"ConstructorArguments" => "",
"EVMVersion" => "",
"ExternalLibraries" => "",
"OptimizationRuns" => ""
}
]
@ -439,7 +420,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
end
test "with a verified contract address", %{conn: conn} do
contract = insert(:smart_contract, optimization: true)
contract = insert(:smart_contract, optimization: true, optimization_runs: 200, evm_version: "default")
params = %{
"module" => "contract",
@ -456,12 +437,158 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
"DecompiledSourceCode" => "Contract source code not decompiled.",
# The contract's optimization value is true, so the expected value
# for `OptimizationUsed` is "1". If it was false, the expected value
# would be "0".
"DecompilerVersion" => "",
"OptimizationUsed" => "1"
"OptimizationUsed" => "true",
"OptimizationRuns" => 200,
"EVMVersion" => "default"
}
]
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == expected_result
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response)
end
test "with constructor arguments", %{conn: conn} do
contract =
insert(:smart_contract,
optimization: true,
optimization_runs: 200,
evm_version: "default",
constructor_arguments:
"00000000000000000000000008e7592ce0d7ebabf42844b62ee6a878d4e1913e000000000000000000000000e1b6037da5f1d756499e184ca15254a981c92546"
)
params = %{
"module" => "contract",
"action" => "getsourcecode",
"address" => to_string(contract.address_hash)
}
expected_result = [
%{
"Address" => to_string(contract.address_hash),
"SourceCode" =>
"/**\n* Submitted for verification at blockscout.com on #{contract.inserted_at}\n*/\n" <>
contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
"OptimizationUsed" => "true",
"OptimizationRuns" => 200,
"EVMVersion" => "default",
"ConstructorArguments" =>
"00000000000000000000000008e7592ce0d7ebabf42844b62ee6a878d4e1913e000000000000000000000000e1b6037da5f1d756499e184ca15254a981c92546"
}
]
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == expected_result
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response)
end
test "with external library", %{conn: conn} do
smart_contract_bytecode =
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582040d82a7379b1ee1632ad4d8a239954fd940277b25628ead95259a85c5eddb2120029"
created_contract_address =
insert(
:address,
hash: "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c",
contract_code: smart_contract_bytecode
)
transaction =
:transaction
|> insert()
|> with_block()
insert(
:internal_transaction_create,
transaction: transaction,
index: 0,
created_contract_address: created_contract_address,
created_contract_code: smart_contract_bytecode,
block_number: transaction.block_number,
block_hash: transaction.block_hash,
block_index: 0,
transaction_index: transaction.index
)
valid_attrs = %{
address_hash: "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c",
name: "Test",
compiler_version: "0.4.23",
contract_source_code:
"pragma solidity ^0.4.23; contract SimpleStorage {uint storedData; function set(uint x) public {storedData = x; } function get() public constant returns (uint) {return storedData; } }",
abi: [
%{
"constant" => false,
"inputs" => [%{"name" => "x", "type" => "uint256"}],
"name" => "set",
"outputs" => [],
"payable" => false,
"stateMutability" => "nonpayable",
"type" => "function"
},
%{
"constant" => true,
"inputs" => [],
"name" => "get",
"outputs" => [%{"name" => "", "type" => "uint256"}],
"payable" => false,
"stateMutability" => "view",
"type" => "function"
}
],
optimization: true,
optimization_runs: 200,
evm_version: "default"
}
external_libraries = [
%SmartContract.ExternalLibrary{:address_hash => "0xb18aed9518d735482badb4e8b7fd8d2ba425ce95", :name => "Test"},
%SmartContract.ExternalLibrary{:address_hash => "0x283539e1b1daf24cdd58a3e934d55062ea663c3f", :name => "Test2"}
]
{:ok, %SmartContract{} = contract} = Chain.create_smart_contract(valid_attrs, external_libraries)
params = %{
"module" => "contract",
"action" => "getsourcecode",
"address" => to_string(contract.address_hash)
}
expected_result = [
%{
"Address" => to_string(contract.address_hash),
"SourceCode" =>
"/**\n* Submitted for verification at blockscout.com on #{contract.inserted_at}\n*/\n" <>
contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
"OptimizationUsed" => "true",
"OptimizationRuns" => 200,
"EVMVersion" => "default",
"ExternalLibraries" => [
%{"name" => "Test", "address_hash" => "0xb18aed9518d735482badb4e8b7fd8d2ba425ce95"},
%{"name" => "Test2", "address_hash" => "0x283539e1b1daf24cdd58a3e934d55062ea663c3f"}
]
}
]
@ -508,9 +635,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
"ABI" => Jason.encode!(contract_code_info.abi),
"ContractName" => contract_code_info.name,
"CompilerVersion" => contract_code_info.version,
"DecompiledSourceCode" => "Contract source code not decompiled.",
"DecompilerVersion" => "",
"OptimizationUsed" => "0"
"OptimizationUsed" => "false",
"EVMVersion" => nil
}
assert response["status"] == "1"
@ -521,7 +647,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
test "with external libraries", %{conn: conn} do
contract_data =
"#{File.cwd!()}/test/support/fixture/smart_contract/compiler_tests.json"
"#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json"
|> File.read!()
|> Jason.decode!()
|> List.first()
@ -578,9 +704,9 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
contract_source_code
assert result["ContractName"] == name
assert result["DecompiledSourceCode"] == "Contract source code not decompiled."
assert result["DecompilerVersion"] == ""
assert result["OptimizationUsed"] == "1"
assert result["DecompiledSourceCode"] == nil
assert result["DecompilerVersion"] == nil
assert result["OptimizationUsed"] == "true"
assert :ok = ExJsonSchema.Validator.validate(verify_schema(), response)
end
end

@ -76,7 +76,7 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do
block = insert(:block, number: 0)
transaction = insert(:transaction, from_address: address) |> with_block(block)
insert(:log, address: address, transaction: transaction, data: "0x010101")
insert(:log, block: block, address: address, transaction: transaction, data: "0x010101")
params = params(api_params, [%{"address" => to_string(address.hash)}])
@ -94,7 +94,7 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do
block = insert(:block, number: 0)
transaction = insert(:transaction, from_address: address) |> with_block(block)
insert(:log, address: address, transaction: transaction, data: "0x010101", first_topic: "0x01")
insert(:log, block: block, address: address, transaction: transaction, data: "0x010101", first_topic: "0x01")
params = params(api_params, [%{"address" => to_string(address.hash), "topics" => ["0x01"]}])
@ -112,8 +112,8 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do
block = insert(:block, number: 0)
transaction = insert(:transaction, from_address: address) |> with_block(block)
insert(:log, address: address, transaction: transaction, data: "0x010101", first_topic: "0x01")
insert(:log, address: address, transaction: transaction, data: "0x020202", first_topic: "0x00")
insert(:log, address: address, block: block, transaction: transaction, data: "0x010101", first_topic: "0x01")
insert(:log, address: address, block: block, transaction: transaction, data: "0x020202", first_topic: "0x00")
params = params(api_params, [%{"address" => to_string(address.hash), "topics" => [["0x01", "0x00"]]}])
@ -127,14 +127,15 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do
test "paginates logs", %{conn: conn, api_params: api_params} do
contract_address = insert(:contract_address)
block = insert(:block)
transaction =
:transaction
|> insert(to_address: contract_address)
|> with_block()
|> with_block(block)
inserted_records =
insert_list(2000, :log, address: contract_address, transaction: transaction, first_topic: "0x01")
insert_list(2000, :log, block: block, address: contract_address, transaction: transaction, first_topic: "0x01")
params = params(api_params, [%{"address" => to_string(contract_address), "topics" => [["0x01"]]}])
@ -191,10 +192,11 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do
transaction: transaction,
data: "0x010101",
first_topic: "0x01",
second_topic: "0x02"
second_topic: "0x02",
block: block
)
insert(:log, address: address, transaction: transaction, data: "0x020202", first_topic: "0x01")
insert(:log, block: block, address: address, transaction: transaction, data: "0x020202", first_topic: "0x01")
params = params(api_params, [%{"address" => to_string(address.hash), "topics" => ["0x01", "0x02"]}])
@ -219,7 +221,8 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do
transaction: transaction,
data: "0x010101",
first_topic: "0x01",
second_topic: "0x02"
second_topic: "0x02",
block: block
)
insert(:log,
@ -227,7 +230,8 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do
transaction: transaction,
data: "0x020202",
first_topic: "0x01",
second_topic: "0x03"
second_topic: "0x03",
block: block
)
params = params(api_params, [%{"address" => to_string(address.hash), "topics" => ["0x01", ["0x02", "0x03"]]}])
@ -341,11 +345,11 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do
transaction2 = insert(:transaction, from_address: address) |> with_block(block2)
transaction3 = insert(:transaction, from_address: address) |> with_block(block3)
insert(:log, address: address, transaction: transaction1, data: "0x010101")
insert(:log, block: block1, address: address, transaction: transaction1, data: "0x010101")
insert(:log, address: address, transaction: transaction2, data: "0x020202")
insert(:log, block: block2, address: address, transaction: transaction2, data: "0x020202")
insert(:log, address: address, transaction: transaction3, data: "0x030303")
insert(:log, block: block3, address: address, transaction: transaction3, data: "0x030303")
changeset = Ecto.Changeset.change(block3, %{consensus: false})

@ -104,24 +104,41 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do
end
end
describe "ethsupply" do
test "returns total supply from DB", %{conn: conn} do
params = %{
"module" => "stats",
"action" => "ethsupply"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == "6"
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(ethsupply_schema(), response)
end
end
# todo: Temporarily disable this test because of unstable work in CI
# describe "ethsupply" do
# test "returns total supply from DB", %{conn: conn} do
# params = %{
# "module" => "stats",
# "action" => "ethsupply"
# }
# assert response =
# conn
# |> get("/api", params)
# |> json_response(200)
# assert response["result"] == "0"
# assert response["status"] == "1"
# assert response["message"] == "OK"
# assert :ok = ExJsonSchema.Validator.validate(ethsupply_schema(), response)
# end
# end
# describe "coinsupply" do
# test "returns total supply minus a burnt number from DB in coins denomination", %{conn: conn} do
# params = %{
# "module" => "stats",
# "action" => "coinsupply"
# }
# assert response =
# conn
# |> get("/api", params)
# |> json_response(200)
# assert response == 0.0
# end
# end
describe "ethprice" do
setup :set_mox_global

@ -243,8 +243,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
transaction_details = [
status: :error,
error: error,
internal_transactions_indexed_at: DateTime.utc_now()
error: error
]
transaction =
@ -256,7 +255,9 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
transaction: transaction,
index: 0,
type: :reward,
error: error
error: error,
block_hash: transaction.block_hash,
block_index: 0
]
insert(:internal_transaction, internal_transaction_details)
@ -285,8 +286,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
test "with a txhash with failed status but awaiting internal transactions", %{conn: conn} do
transaction_details = [
status: :error,
error: nil,
internal_transactions_indexed_at: nil
error: nil
]
transaction =
@ -411,7 +411,9 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
address: address,
transaction: transaction,
first_topic: "first topic",
second_topic: "second topic"
second_topic: "second topic",
block: block,
block_number: block.number
)
end)
@ -486,7 +488,9 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
address: address,
transaction: transaction,
first_topic: "first topic",
second_topic: "second topic"
second_topic: "second topic",
block: block,
block_number: block.number
)
params = %{

@ -28,7 +28,7 @@ defmodule BlockScoutWeb.API.V1.VerifiedControllerTest do
test "verifying a smart contract with external libraries", %{conn: conn} do
contract_data =
"#{File.cwd!()}/test/support/fixture/smart_contract/compiler_tests.json"
"#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json"
|> File.read!()
|> Jason.decode!()
|> List.first()

@ -44,14 +44,18 @@ defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
transaction: transaction,
index: 0,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 0
)
insert(:internal_transaction,
transaction: transaction,
index: 1,
transaction_index: transaction.index,
block_number: transaction.block_number
block_number: transaction.block_number,
block_hash: transaction.block_hash,
block_index: 1
)
path = transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, transaction.hash)
@ -64,7 +68,8 @@ defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
assert json_response(conn, 200)
assert Enum.count(items) == 2
# excluding of internal transactions with type=call and index=0
assert Enum.count(items) == 1
end
test "includes USD exchange rate value for address in assigns", %{conn: conn} do
@ -90,7 +95,9 @@ defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
transaction: transaction,
index: 0,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 0
)
|> with_contract_creation(contract_address)
@ -118,7 +125,9 @@ defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
transaction: transaction,
index: 0,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 0
)
second_page_indexes =
@ -128,7 +137,9 @@ defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
transaction: transaction,
index: index,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: index
)
end)
|> Enum.map(& &1.index)
@ -161,7 +172,9 @@ defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
transaction: transaction,
index: index,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: index
)
end)
@ -190,7 +203,9 @@ defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
transaction: transaction,
index: index,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: index
)
end)

@ -29,7 +29,13 @@ defmodule BlockScoutWeb.TransactionLogControllerTest do
|> with_block()
address = insert(:address)
insert(:log, address: address, transaction: transaction)
insert(:log,
address: address,
transaction: transaction,
block: transaction.block,
block_number: transaction.block_number
)
conn = get(conn, transaction_log_path(conn, :index, transaction), %{type: "JSON"})
@ -46,7 +52,13 @@ defmodule BlockScoutWeb.TransactionLogControllerTest do
|> with_block()
address = insert(:address)
insert(:log, address: address, transaction: transaction)
insert(:log,
address: address,
transaction: transaction,
block: transaction.block,
block_number: transaction.block_number
)
conn = get(conn, transaction_log_path(conn, :index, transaction), %{type: "JSON"})
@ -73,11 +85,24 @@ defmodule BlockScoutWeb.TransactionLogControllerTest do
|> insert()
|> with_block()
log = insert(:log, transaction: transaction, index: 1)
log =
insert(:log,
transaction: transaction,
index: 1,
block: transaction.block,
block_number: transaction.block_number
)
second_page_indexes =
2..51
|> Enum.map(fn index -> insert(:log, transaction: transaction, index: index) end)
|> Enum.map(fn index ->
insert(:log,
transaction: transaction,
index: index,
block: transaction.block,
block_number: transaction.block_number
)
end)
|> Enum.map(& &1.index)
conn =
@ -98,7 +123,14 @@ defmodule BlockScoutWeb.TransactionLogControllerTest do
|> with_block()
1..60
|> Enum.map(fn index -> insert(:log, transaction: transaction, index: index) end)
|> Enum.map(fn index ->
insert(:log,
transaction: transaction,
index: index,
block: transaction.block,
block_number: transaction.block_number
)
end)
conn = get(conn, transaction_log_path(conn, :index, transaction), %{type: "JSON"})

@ -46,11 +46,11 @@ defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do
end
test "includes token transfers for the transaction", %{conn: conn} do
transaction = insert(:transaction)
transaction = insert(:transaction) |> with_block()
insert(:token_transfer, transaction: transaction)
insert(:token_transfer, transaction: transaction, block: transaction.block, block_number: transaction.block_number)
insert(:token_transfer, transaction: transaction)
insert(:token_transfer, transaction: transaction, block: transaction.block, block_number: transaction.block_number)
path = transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash)
@ -106,7 +106,9 @@ defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do
insert(
:token_transfer,
transaction: transaction,
log_index: log_index
log_index: log_index,
block: transaction.block,
block_number: transaction.block_number
)
end)
@ -129,7 +131,9 @@ defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do
insert(
:token_transfer,
transaction: transaction,
log_index: log_index
log_index: log_index,
block_number: transaction.block_number,
block: transaction.block
)
end)

@ -80,7 +80,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
test "see the contract creator and transaction links", %{session: session} do
address = insert(:address)
contract = insert(:contract_address)
transaction = insert(:transaction, from_address: address, created_contract_address: contract)
transaction = insert(:transaction, from_address: address, created_contract_address: contract) |> with_block()
internal_transaction =
insert(
@ -88,7 +88,9 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
index: 1,
transaction: transaction,
from_address: address,
created_contract_address: contract
created_contract_address: contract,
block_hash: transaction.block_hash,
block_index: 1
)
address_hash = AddressView.trimmed_hash(address.hash)
@ -102,7 +104,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
test "see the contract creator and transaction links even when the creator is another contract", %{session: session} do
lincoln = insert(:address)
contract = insert(:contract_address)
transaction = insert(:transaction)
transaction = insert(:transaction) |> with_block()
another_contract = insert(:contract_address)
insert(
@ -112,7 +114,9 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
from_address: lincoln,
to_address: contract,
created_contract_address: contract,
type: :call
type: :call,
block_hash: transaction.block_hash,
block_index: 1
)
internal_transaction =
@ -121,7 +125,9 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
index: 2,
transaction: transaction,
from_address: contract,
created_contract_address: another_contract
created_contract_address: another_contract,
block_hash: transaction.block_hash,
block_index: 2
)
contract_hash = AddressView.trimmed_hash(contract.hash)
@ -208,7 +214,9 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
to_address: address,
index: 1,
block_number: 7000,
transaction_index: 1
transaction_index: 1,
block_hash: transaction.block_hash,
block_index: 1
)
insert(:internal_transaction,
@ -216,7 +224,9 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
from_address: address,
index: 2,
block_number: 8000,
transaction_index: 2
transaction_index: 2,
block_hash: transaction.block_hash,
block_index: 2
)
{:ok, %{internal_transaction_lincoln_to_address: internal_transaction_lincoln_to_address}}
@ -251,7 +261,9 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
index: 2,
from_address: addresses.lincoln,
block_number: transaction.block_number,
transaction_index: transaction.index
transaction_index: transaction.index,
block_hash: transaction.block_hash,
block_index: 2
)
Notifier.handle_event({:chain_event, :internal_transactions, :realtime, [internal_transaction]})

@ -7,7 +7,7 @@ defmodule BlockScoutWeb.ViewingAppTest do
alias BlockScoutWeb.Counters.BlocksIndexedCounter
alias Explorer.Counters.AddressesCounter
alias Explorer.{Repo}
alias Explorer.Chain.{Transaction}
alias Explorer.Chain.PendingBlockOperation
setup do
start_supervised!(AddressesCounter)
@ -29,6 +29,8 @@ defmodule BlockScoutWeb.ViewingAppTest do
assert Decimal.cmp(Explorer.Chain.indexed_ratio(), Decimal.from_float(0.5)) == :eq
insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true)
session
|> AppPage.visit_page()
|> assert_has(AppPage.indexed_status("50% Blocks Indexed"))
@ -46,6 +48,8 @@ defmodule BlockScoutWeb.ViewingAppTest do
assert Decimal.cmp(Explorer.Chain.indexed_ratio(), 1) == :eq
insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true)
session
|> AppPage.visit_page()
|> assert_has(AppPage.indexed_status("Indexing Tokens"))
@ -65,6 +69,8 @@ defmodule BlockScoutWeb.ViewingAppTest do
assert Decimal.cmp(Explorer.Chain.indexed_ratio(), Decimal.from_float(0.5)) == :eq
insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true)
session
|> AppPage.visit_page()
|> assert_has(AppPage.indexed_status("50% Blocks Indexed"))
@ -90,6 +96,8 @@ defmodule BlockScoutWeb.ViewingAppTest do
assert Decimal.cmp(Explorer.Chain.indexed_ratio(), Decimal.from_float(0.9)) == :eq
insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true)
session
|> AppPage.visit_page()
|> assert_has(AppPage.indexed_status("90% Blocks Indexed"))
@ -111,6 +119,10 @@ defmodule BlockScoutWeb.ViewingAppTest do
|> insert()
|> with_block(block)
block_hash = block.hash
insert(:pending_block_operation, block_hash: block_hash, fetch_internal_transactions: true)
BlocksIndexedCounter.calculate_blocks_indexed()
assert Decimal.cmp(Explorer.Chain.indexed_ratio(), 1) == :eq
@ -119,7 +131,10 @@ defmodule BlockScoutWeb.ViewingAppTest do
|> AppPage.visit_page()
|> assert_has(AppPage.indexed_status("Indexing Tokens"))
Repo.update_all(Transaction, set: [internal_transactions_indexed_at: DateTime.utc_now()])
Repo.update_all(
from(p in PendingBlockOperation, where: p.block_hash == ^block_hash),
set: [fetch_internal_transactions: false]
)
BlocksIndexedCounter.calculate_blocks_indexed()

@ -59,7 +59,12 @@ defmodule BlockScoutWeb.ViewingBlocksTest do
internal_transaction =
:internal_transaction_create
|> insert(transaction: transaction, index: 0)
|> insert(
transaction: transaction,
index: 0,
block_hash: transaction.block_hash,
block_index: 1
)
|> with_contract_creation(contract_address)
session

@ -44,9 +44,15 @@ defmodule BlockScoutWeb.ViewingTransactionsTest do
)
|> with_block(block, gas_used: Decimal.new(1_230_000_000_000_123_000), status: :ok)
insert(:log, address: lincoln, index: 0, transaction: transaction)
internal = insert(:internal_transaction, index: 0, transaction: transaction)
insert(:log, address: lincoln, index: 0, transaction: transaction, block: block, block_number: block.number)
internal =
insert(:internal_transaction,
index: 0,
transaction: transaction,
block_hash: transaction.block_hash,
block_index: 0
)
{:ok,
%{
@ -95,7 +101,7 @@ defmodule BlockScoutWeb.ViewingTransactionsTest do
|> with_contract_creation(contract_address)
:internal_transaction_create
|> insert(transaction: transaction, index: 0)
|> insert(transaction: transaction, index: 0, block_hash: transaction.block_hash, block_index: 0)
|> with_contract_creation(contract_address)
session

@ -58,9 +58,15 @@ defmodule BlockScoutWeb.Schema.Query.NodeTest do
end
test "with valid argument 'id' for an internal transaction", %{conn: conn} do
transaction = insert(:transaction)
transaction = insert(:transaction) |> with_block()
internal_transaction = insert(:internal_transaction, transaction: transaction, index: 0)
internal_transaction =
insert(:internal_transaction,
transaction: transaction,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
query = """
query($id: ID!) {
@ -96,9 +102,15 @@ defmodule BlockScoutWeb.Schema.Query.NodeTest do
end
test "with 'id' for non-existent internal transaction", %{conn: conn} do
transaction = build(:transaction)
internal_transaction = build(:internal_transaction, transaction: transaction, index: 0)
transaction = insert(:transaction) |> with_block()
internal_transaction =
build(:internal_transaction,
transaction: transaction,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
query = """
query($id: ID!) {

@ -134,7 +134,9 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
transaction: transaction,
index: 0,
from_address: address,
call_type: :call
call_type: :call,
block_hash: transaction.block_hash,
block_index: 0
}
internal_transaction =
@ -259,11 +261,28 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
end
test "internal transactions are ordered by ascending index", %{conn: conn} do
transaction = insert(:transaction)
transaction = insert(:transaction) |> with_block()
insert(:internal_transaction,
transaction: transaction,
index: 2,
block_hash: transaction.block_hash,
block_index: 2
)
insert(:internal_transaction,
transaction: transaction,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
insert(:internal_transaction, transaction: transaction, index: 2)
insert(:internal_transaction, transaction: transaction, index: 0)
insert(:internal_transaction, transaction: transaction, index: 1)
insert(:internal_transaction,
transaction: transaction,
index: 1,
block_hash: transaction.block_hash,
block_index: 1
)
query = """
query ($hash: FullHash!, $first: Int!) {
@ -370,11 +389,28 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
#
# This test ensures support for a 'count' argument.
transaction = insert(:transaction)
transaction = insert(:transaction) |> with_block()
insert(:internal_transaction, transaction: transaction, index: 2)
insert(:internal_transaction, transaction: transaction, index: 0)
insert(:internal_transaction, transaction: transaction, index: 1)
insert(:internal_transaction,
transaction: transaction,
index: 2,
block_hash: transaction.block_hash,
block_index: 2
)
insert(:internal_transaction,
transaction: transaction,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
insert(:internal_transaction,
transaction: transaction,
index: 1,
block_hash: transaction.block_hash,
block_index: 1
)
query = """
query ($hash: FullHash!, $last: Int!, $count: Int!) {
@ -407,10 +443,15 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
end
test "pagination support with 'first' and 'after' arguments", %{conn: conn} do
transaction = insert(:transaction)
transaction = insert(:transaction) |> with_block()
for index <- 0..5 do
insert(:internal_transaction_create, transaction: transaction, index: index)
insert(:internal_transaction_create,
transaction: transaction,
index: index,
block_hash: transaction.block_hash,
block_index: index
)
end
query1 = """

@ -11,14 +11,16 @@ defmodule BlockScoutWeb.AddressViewTest do
end
test "for a pending internal transaction contract creation to address" do
transaction = insert(:transaction, to_address: nil)
transaction = insert(:transaction, to_address: nil) |> with_block()
internal_transaction =
insert(:internal_transaction,
index: 1,
transaction: transaction,
to_address: nil,
created_contract_address_hash: nil
created_contract_address_hash: nil,
block_hash: transaction.block_hash,
block_index: 1
)
assert "Contract Address Pending" == AddressView.address_partial_selector(internal_transaction, :to, nil)

@ -14,7 +14,7 @@ defmodule BlockScoutWeb.ApiDocsViewTest do
test "adds slash before path" do
Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint,
url: [scheme: "https", host: "blockscout.com", port: 9999, path: "/chain/dog"]
url: [scheme: "https", host: "blockscout.com", port: 9999, api_path: "/chain/dog"]
)
assert APIDocsView.api_url() == "https://blockscout.com/chain/dog/api"
@ -22,7 +22,7 @@ defmodule BlockScoutWeb.ApiDocsViewTest do
test "does not add slash to empty path" do
Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint,
url: [scheme: "https", host: "blockscout.com", port: 9999, path: ""]
url: [scheme: "https", host: "blockscout.com", port: 9999, api_path: ""]
)
assert APIDocsView.api_url() == "https://blockscout.com/api"
@ -49,7 +49,7 @@ defmodule BlockScoutWeb.ApiDocsViewTest do
test "adds slash before path" do
Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint,
url: [scheme: "https", host: "blockscout.com", port: 9999, path: "/chain/dog"]
url: [scheme: "https", host: "blockscout.com", port: 9999, api_path: "/chain/dog"]
)
assert APIDocsView.eth_rpc_api_url() == "https://blockscout.com/chain/dog/api/eth_rpc"

@ -192,6 +192,8 @@ defmodule BlockScoutWeb.TransactionViewTest do
|> insert()
|> with_block(block, status: :error)
insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true)
status = TransactionView.transaction_status(transaction)
assert TransactionView.formatted_status(status) == "Error: (Awaiting internal transactions for reason)"
end
@ -200,7 +202,7 @@ defmodule BlockScoutWeb.TransactionViewTest do
transaction =
:transaction
|> insert()
|> with_block(status: :error, internal_transactions_indexed_at: DateTime.utc_now(), error: "Out of Gas")
|> with_block(status: :error, error: "Out of Gas")
status = TransactionView.transaction_status(transaction)
assert TransactionView.formatted_status(status) == "Error: Out of Gas"

@ -0,0 +1,13 @@
[
{
"compiler_version": "v0.5.11+commit.c082d0b4",
"contract": "pragma solidity 0.5.11;library BadSafeMath { function add(uint256 a, uint256 b) public pure returns (uint256) { uint256 c = a + 2 * b; require(c >= a, \"SafeMath: addition overflow\"); return c; }}contract SimpleStorage { uint256 storedData = 10; using BadSafeMath for uint256; function increment(uint256 x) public { storedData = storedData.add(x); } function set(uint256 x) public { storedData = x; } function get() public view returns (uint256) { return storedData; }}",
"expected_bytecode": "608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054733662e222908fa35f013bee37695d0510098b6d7363771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a723158203e59bfb9a5a2e55d38231922c86d8b2ec9b66cb2f6595613674bc4e15290b60764736f6c634300050b0032",
"external_libraries": {
"BadSafeMath": "0x3662e222908fa35f013bee37695d0510098b6d73"
},
"name": "SimpleStorage",
"optimize": true
}
]

@ -437,7 +437,8 @@ defmodule EthereumJSONRPC.Block do
end
defp entry_to_elixir({key, quantity})
when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size totalDifficulty) and not is_nil(quantity) do
when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size totalDifficulty paidFees) and
not is_nil(quantity) do
{key, quantity_to_integer(quantity)}
end
@ -451,7 +452,7 @@ defmodule EthereumJSONRPC.Block do
# hash format
defp entry_to_elixir({key, _} = entry)
when key in ~w(author extraData hash logsBloom miner mixHash nonce parentHash receiptsRoot sealFields sha3Uncles
signature stateRoot step transactionsRoot uncles),
signature stateRoot step transactionsRoot uncles bitcoinMergedMiningCoinbaseTransaction bitcoinMergedMiningHeader bitcoinMergedMiningMerkleProof hashForMergedMining),
do: entry
defp entry_to_elixir({"timestamp" = key, timestamp}) do

@ -39,6 +39,7 @@ defmodule EthereumJSONRPC.Log do
...> )
%{
address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b",
block_hash: "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
block_number: 37,
data: "0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
first_topic: "0x600bcf04a13e752d1e3670a5a9f1c21177ca2a93c6f5391d4f1298d098097c22",
@ -47,6 +48,7 @@ defmodule EthereumJSONRPC.Log do
second_topic: nil,
third_topic: nil,
transaction_hash: "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5",
block_hash: "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
type: "mined"
}
@ -69,7 +71,9 @@ defmodule EthereumJSONRPC.Log do
...> )
%{
address_hash: "0xda8b3276cde6d768a44b9dac659faa339a41ac55",
block_hash: "0x0b89f7f894f5d8ba941e16b61490e999a0fcaaf92dfcc70aee2ac5ddb5f243e1",
block_number: 4448,
block_hash: "0x0b89f7f894f5d8ba941e16b61490e999a0fcaaf92dfcc70aee2ac5ddb5f243e1",
data: "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
first_topic: "0xadc1e8a294f8415511303acc4a8c0c5906c7eb0bf2a71043d7f4b03b46a39130",
fourth_topic: nil,
@ -84,6 +88,7 @@ defmodule EthereumJSONRPC.Log do
%{
"address" => address_hash,
"blockNumber" => block_number,
"blockHash" => block_hash,
"data" => data,
"logIndex" => index,
"topics" => topics,
@ -93,6 +98,7 @@ defmodule EthereumJSONRPC.Log do
%{
address_hash: address_hash,
block_number: block_number,
block_hash: block_hash,
data: data,
index: index,
transaction_hash: transaction_hash

@ -45,7 +45,7 @@ defmodule EthereumJSONRPC.Parity.Trace.Action do
end
defp entry_to_elixir({key, value} = entry)
when key in ~w(address callType from init input refundAddress to) and is_binary(value),
when key in ~w(address callType from init input refundAddress to creationMethod) and is_binary(value),
do: entry
defp entry_to_elixir({key, quantity}) when key in ~w(balance gas value) do

@ -34,6 +34,7 @@ defmodule EthereumJSONRPC.ReceiptsTest do
cumulative_gas_used: 884_322,
address_hash: "0x1e2fbe6be9eb39fc894d38be976111f332172d83",
block_number: 3_560_000,
block_hash: nil,
data:
"0x00000000000000000000000033066f6a8adf2d4f5db193524b6fbae062ec0d110000000000000000000000000000000000000000000000000000000000001030",
index: 12,
@ -48,6 +49,7 @@ defmodule EthereumJSONRPC.ReceiptsTest do
EthereumJSONRPC.Parity ->
%{
created_contract_address_hash: nil,
block_hash: nil,
cumulative_gas_used: 50450,
gas_used: 50450,
address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b",
@ -82,7 +84,9 @@ defmodule EthereumJSONRPC.ReceiptsTest do
"logs" => [
%{
"address" => address_hash,
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => integer_to_quantity(block_number),
"blockHash" => nil,
"data" => data,
"logIndex" => integer_to_quantity(index),
"topics" => [first_topic],

@ -12,7 +12,7 @@ config :explorer,
token_functions_reader_max_retries: 3,
allowed_evm_versions:
System.get_env("ALLOWED_EVM_VERSIONS") ||
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,default",
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,default",
include_uncles_in_average_block_time:
if(System.get_env("UNCLES_IN_AVERAGE_BLOCK_TIME") == "true", do: true, else: false),
healthy_blocks_period: System.get_env("HEALTHY_BLOCKS_PERIOD") || :timer.minutes(5),
@ -66,6 +66,11 @@ config :explorer, Explorer.Chain.Cache.AddressSum,
ttl_check_interval: :timer.seconds(1),
global_ttl: address_sum_global_ttl
config :explorer, Explorer.Chain.Cache.AddressSumMinusBurnt,
enabled: true,
ttl_check_interval: :timer.seconds(1),
global_ttl: address_sum_global_ttl
balances_update_interval =
if System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL") do
case Integer.parse(System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL")) do

@ -3,7 +3,7 @@ defmodule Explorer.Admin.Administrator.Role do
Supported roles for an administrator.
"""
@behaviour Ecto.Type
use Ecto.Type
@typedoc """
Supported role atoms for an administrator.

@ -10,6 +10,7 @@ defmodule Explorer.Application do
alias Explorer.Chain.Cache.{
Accounts,
AddressSum,
AddressSumMinusBurnt,
BlockCount,
BlockNumber,
Blocks,
@ -48,6 +49,7 @@ defmodule Explorer.Application do
{Admin.Recovery, [[], [name: Admin.Recovery]]},
TransactionCount,
AddressSum,
AddressSumMinusBurnt,
BlockCount,
Blocks,
NetVersion,

@ -15,6 +15,7 @@ defmodule Explorer.Chain do
preload: 2,
select: 2,
subquery: 1,
union: 2,
union_all: 2,
where: 2,
where: 3
@ -38,6 +39,7 @@ defmodule Explorer.Chain do
Import,
InternalTransaction,
Log,
PendingBlockOperation,
SmartContract,
StakingPool,
Token,
@ -193,8 +195,66 @@ defmodule Explorer.Chain do
direction = Keyword.get(options, :direction)
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
InternalTransaction
|> InternalTransaction.where_address_fields_match(hash, direction)
if direction == nil do
query_to_address_hash_wrapped =
InternalTransaction
|> InternalTransaction.where_address_fields_match(hash, :to_address_hash)
|> common_where_limit_order(paging_options)
|> wrapped_union_subquery()
query_from_address_hash_wrapped =
InternalTransaction
|> InternalTransaction.where_address_fields_match(hash, :from_address_hash)
|> common_where_limit_order(paging_options)
|> wrapped_union_subquery()
query_created_contract_address_hash_wrapped =
InternalTransaction
|> InternalTransaction.where_address_fields_match(hash, :created_contract_address_hash)
|> common_where_limit_order(paging_options)
|> wrapped_union_subquery()
full_query =
query_to_address_hash_wrapped
|> union(^query_from_address_hash_wrapped)
|> union(^query_created_contract_address_hash_wrapped)
full_wrapped_query =
from(
q in subquery(full_query),
select: q
)
full_wrapped_query
|> order_by(
[q],
desc: q.block_number,
desc: q.transaction_index,
desc: q.index
)
|> preload(transaction: :block)
|> join_associations(necessity_by_association)
|> Repo.all()
else
InternalTransaction
|> InternalTransaction.where_nonpending_block()
|> InternalTransaction.where_address_fields_match(hash, direction)
|> common_where_limit_order(paging_options)
|> preload(transaction: :block)
|> join_associations(necessity_by_association)
|> Repo.all()
end
end
def wrapped_union_subquery(query) do
from(
q in subquery(query),
select: q
)
end
defp common_where_limit_order(query, paging_options) do
query
|> InternalTransaction.where_is_different_from_parent_transaction()
|> InternalTransaction.where_block_number_is_not_null()
|> page_internal_transaction(paging_options)
@ -205,9 +265,6 @@ defmodule Explorer.Chain do
desc: it.transaction_index,
desc: it.index
)
|> preload(transaction: :block)
|> join_associations(necessity_by_association)
|> Repo.all()
end
@doc """
@ -261,10 +318,10 @@ defmodule Explorer.Chain do
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
if Application.get_env(:block_scout_web, BlockScoutWeb.Chain)[:has_emission_funds] do
blocks_range = address_to_transactions_tasks_range_of_blocks(address_hash, options)
rewards_task =
Task.async(fn ->
Reward.fetch_emission_rewards_tuples(address_hash, paging_options)
end)
Task.async(fn -> Reward.fetch_emission_rewards_tuples(address_hash, paging_options, blocks_range) end)
[rewards_task | address_to_transactions_tasks(address_hash, options)]
|> wait_for_address_transactions()
@ -303,21 +360,72 @@ defmodule Explorer.Chain do
|> Enum.take(paging_options.page_size)
end
defp address_to_transactions_tasks_query(options) do
options
|> Keyword.get(:paging_options, @default_paging_options)
|> fetch_transactions()
end
defp address_to_transactions_tasks(address_hash, options) do
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
direction = Keyword.get(options, :direction)
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
base_query =
paging_options
|> fetch_transactions()
|> join_associations(necessity_by_association)
base_query
options
|> address_to_transactions_tasks_query()
|> join_associations(necessity_by_association)
|> Transaction.matching_address_queries_list(direction, address_hash)
|> Enum.map(fn query -> Task.async(fn -> Repo.all(query) end) end)
end
defp address_to_transactions_tasks_range_of_blocks(address_hash, options) do
direction = Keyword.get(options, :direction)
extremums_list =
options
|> address_to_transactions_tasks_query()
|> Transaction.matching_address_queries_list(direction, address_hash)
|> Enum.map(fn query ->
max_query =
from(
q in subquery(query),
select: %{min_block_number: min(q.block_number), max_block_number: max(q.block_number)}
)
max_query
|> Repo.one!()
end)
extremums_list
|> Enum.reduce(%{min_block_number: nil, max_block_number: 0}, fn %{
min_block_number: min_number,
max_block_number: max_number
},
extremums_result ->
current_min_number = Map.get(extremums_result, :min_block_number)
current_max_number = Map.get(extremums_result, :max_block_number)
extremums_result =
if is_number(current_min_number) do
if is_number(min_number) and min_number > 0 and min_number < current_min_number do
extremums_result
|> Map.put(:min_block_number, min_number)
else
extremums_result
end
else
extremums_result
|> Map.put(:min_block_number, min_number)
end
if is_number(max_number) and max_number > 0 and max_number > current_max_number do
extremums_result
|> Map.put(:max_block_number, max_number)
else
extremums_result
end
end)
end
defp wait_for_address_transactions(tasks) do
tasks
|> Task.yield_many(:timer.seconds(20))
@ -355,9 +463,9 @@ defmodule Explorer.Chain do
base_query =
from(log in Log,
inner_join: transaction in assoc(log, :transaction),
order_by: [desc: transaction.block_number, desc: transaction.index],
preload: [:transaction, transaction: [to_address: :smart_contract]],
inner_join: transaction in Transaction,
on: transaction.hash == log.transaction_hash,
order_by: [desc: log.block_number, desc: log.index],
where: transaction.block_number < ^block_number,
or_where: transaction.block_number == ^block_number and transaction.index > ^transaction_index,
or_where:
@ -368,7 +476,19 @@ defmodule Explorer.Chain do
select: log
)
base_query
wrapped_query =
from(
log in subquery(base_query),
inner_join: transaction in Transaction,
preload: [:transaction, transaction: [to_address: :smart_contract]],
where:
log.block_hash == transaction.block_hash and
log.block_number == transaction.block_number and
log.transaction_hash == transaction.hash,
select: log
)
wrapped_query
|> filter_topic(options)
|> Repo.all()
|> Enum.take(paging_options.page_size)
@ -718,33 +838,25 @@ defmodule Explorer.Chain do
end
@doc """
Checks to see if the chain is down indexing based on the transaction from the oldest block having
an `internal_transactions_indexed_at` date.
Checks to see if the chain is down indexing based on the transaction from the
oldest block and the `fetch_internal_transactions` pending operation
"""
@spec finished_indexing?() :: boolean()
def finished_indexing? do
transaction_exists =
Transaction
|> limit(1)
|> Repo.one()
min_block_number_transaction = Repo.aggregate(Transaction, :min, :block_number)
with {:transactions_exist, true} <- {:transactions_exist, Repo.exists?(Transaction)},
min_block_number when not is_nil(min_block_number) <- Repo.aggregate(Transaction, :min, :block_number) do
query =
from(
b in Block,
join: pending_ops in assoc(b, :pending_operations),
where: pending_ops.fetch_internal_transactions,
where: b.consensus and b.number == ^min_block_number
)
if transaction_exists do
if min_block_number_transaction do
Transaction
|> where([t], t.block_number == ^min_block_number_transaction and is_nil(t.internal_transactions_indexed_at))
|> limit(1)
|> Repo.one()
|> case do
nil -> true
_ -> false
end
else
false
end
!Repo.exists?(query)
else
true
{:transactions_exist, false} -> true
nil -> false
end
end
@ -1322,6 +1434,20 @@ defmodule Explorer.Chain do
Repo.one!(query)
end
@spec fetch_sum_coin_total_supply_minus_burnt() :: non_neg_integer
def fetch_sum_coin_total_supply_minus_burnt do
{:ok, burn_address_hash} = string_to_address_hash("0x0000000000000000000000000000000000000000")
query =
from(
a0 in Address,
select: fragment("SUM(a0.fetched_coin_balance)"),
where: a0.hash != ^burn_address_hash
)
Repo.one!(query) || 0
end
@spec fetch_sum_coin_total_supply() :: non_neg_integer
def fetch_sum_coin_total_supply do
query =
@ -1336,11 +1462,8 @@ defmodule Explorer.Chain do
@doc """
The number of `t:Explorer.Chain.InternalTransaction.t/0`.
iex> transaction =
...> :transaction |>
...> insert() |>
...> with_block()
iex> insert(:internal_transaction, index: 0, transaction: transaction)
iex> transaction = :transaction |> insert() |> with_block()
iex> insert(:internal_transaction, index: 0, transaction: transaction, block_hash: transaction.block_hash, block_index: 0)
iex> Explorer.Chain.internal_transaction_count()
1
@ -1351,7 +1474,7 @@ defmodule Explorer.Chain do
"""
def internal_transaction_count do
Repo.one!(from(it in "internal_transactions", select: fragment("COUNT(*)")))
Repo.aggregate(InternalTransaction.where_nonpending_block(), :count, :transaction_hash)
end
@doc """
@ -1627,18 +1750,20 @@ defmodule Explorer.Chain do
end
@doc """
Returns a stream of all blocks with unfetched internal transactions.
Returns a stream of all blocks with unfetched internal transactions, using
the `pending_block_operation` table.
Only blocks with consensus are returned.
iex> non_consensus = insert(:block, consensus: false)
iex> insert(:pending_block_operation, block: non_consensus, fetch_internal_transactions: true)
iex> unfetched = insert(:block)
iex> fetched = insert(:block, internal_transactions_indexed_at: DateTime.utc_now())
iex> to_be_refetched = insert(:block, refetch_needed: true)
iex> insert(:pending_block_operation, block: unfetched, fetch_internal_transactions: true)
iex> fetched = insert(:block)
iex> insert(:pending_block_operation, block: fetched, fetch_internal_transactions: false)
iex> {:ok, number_set} = Explorer.Chain.stream_blocks_with_unfetched_internal_transactions(
...> [:number],
...> MapSet.new(),
...> fn %Explorer.Chain.Block{number: number}, acc ->
...> fn number, acc ->
...> MapSet.put(acc, number)
...> end
...> )
@ -1648,112 +1773,55 @@ defmodule Explorer.Chain do
true
iex> fetched.hash in number_set
false
iex> to_be_refetched.number in number_set
false
"""
@spec stream_blocks_with_unfetched_internal_transactions(
fields :: [
:consensus
| :difficulty
| :gas_limit
| :gas_used
| :hash
| :miner
| :miner_hash
| :nonce
| :number
| :parent_hash
| :size
| :timestamp
| :total_difficulty
| :transactions
| :internal_transactions_indexed_at
],
initial :: accumulator,
reducer :: (entry :: term(), accumulator -> accumulator)
) :: {:ok, accumulator}
when accumulator: term()
def stream_blocks_with_unfetched_internal_transactions(fields, initial, reducer) when is_function(reducer, 2) do
def stream_blocks_with_unfetched_internal_transactions(initial, reducer) when is_function(reducer, 2) do
query =
from(
b in Block,
join: pending_ops in assoc(b, :pending_operations),
where: pending_ops.fetch_internal_transactions,
where: b.consensus,
where: is_nil(b.internal_transactions_indexed_at),
where: not b.refetch_needed,
select: ^fields
select: b.number
)
Repo.stream_reduce(query, initial, reducer)
end
@doc """
Returns a stream of all collated transactions with unfetched internal transactions.
def remove_nonconsensus_blocks_from_pending_ops(block_hashes) do
query =
from(
po in PendingBlockOperation,
where: po.block_hash in ^block_hashes
)
Only transactions that have been collated into a block are returned; pending transactions not in a block are filtered
out.
{_, _} = Repo.delete_all(query)
iex> pending = insert(:transaction)
iex> unfetched_collated =
...> :transaction |>
...> insert() |>
...> with_block()
iex> fetched_collated =
...> :transaction |>
...> insert() |>
...> with_block(internal_transactions_indexed_at: DateTime.utc_now())
iex> {:ok, hash_set} = Explorer.Chain.stream_transactions_with_unfetched_internal_transactions(
...> [:hash],
...> MapSet.new(),
...> fn %Explorer.Chain.Transaction{hash: hash}, acc ->
...> MapSet.put(acc, hash)
...> end
...> )
iex> pending.hash in hash_set
false
iex> unfetched_collated.hash in hash_set
true
iex> fetched_collated.hash in hash_set
false
:ok
end
"""
@spec stream_transactions_with_unfetched_internal_transactions(
fields :: [
:block_hash
| :internal_transactions_indexed_at
| :from_address_hash
| :gas
| :gas_price
| :hash
| :index
| :input
| :nonce
| :r
| :s
| :to_address_hash
| :v
| :value
],
initial :: accumulator,
reducer :: (entry :: term(), accumulator -> accumulator)
) :: {:ok, accumulator}
when accumulator: term()
def stream_transactions_with_unfetched_internal_transactions(fields, initial, reducer) when is_function(reducer, 2) do
def remove_nonconsensus_blocks_from_pending_ops do
query =
from(
t in Transaction,
# exclude pending transactions and replaced transactions
where: not is_nil(t.block_hash) and is_nil(t.internal_transactions_indexed_at),
select: ^fields
po in PendingBlockOperation,
inner_join: block in Block,
on: block.hash == po.block_hash,
where: block.consensus == false
)
Repo.stream_reduce(query, initial, reducer)
{_, _} = Repo.delete_all(query)
:ok
end
@spec stream_transactions_with_unfetched_created_contract_codes(
fields :: [
:block_hash
| :internal_transactions_indexed_at
| :created_contract_code_indexed_at
| :from_address_hash
| :gas
@ -1788,7 +1856,6 @@ defmodule Explorer.Chain do
@spec stream_mined_transactions(
fields :: [
:block_hash
| :internal_transactions_indexed_at
| :created_contract_code_indexed_at
| :from_address_hash
| :gas
@ -1820,7 +1887,6 @@ defmodule Explorer.Chain do
@spec stream_pending_transactions(
fields :: [
:block_hash
| :internal_transactions_indexed_at
| :created_contract_code_indexed_at
| :from_address_hash
| :gas
@ -2202,8 +2268,8 @@ defmodule Explorer.Chain do
## Options
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`:required`, and the `t:Explorer.Chain.InternalTransaction.t/0` has no associated record for that association,
then the `t:Explorer.Chain.InternalTransaction.t/0` will not be included in the list.
`:required`, and the `t:Explorer.Chain.Transaction.t/0` has no associated record for that association,
then the `t:Explorer.Chain.Transaction.t/0` will not be included in the list.
* `:paging_options` - a `t:Explorer.PagingOptions.t/0` used to specify the `:page_size` and
`:key` (a tuple of the lowest/oldest `{block_number, index}`) and. Results will be the transactions older than
the `block_number` and `index` that are passed.
@ -2257,8 +2323,8 @@ defmodule Explorer.Chain do
## Options
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`:required`, and the `t:Explorer.Chain.InternalTransaction.t/0` has no associated record for that association,
then the `t:Explorer.Chain.InternalTransaction.t/0` will not be included in the list.
`:required`, and the `t:Explorer.Chain.Transaction.t/0` has no associated record for that association,
then the `t:Explorer.Chain.Transaction.t/0` will not be included in the list.
* `:paging_options` - a `t:Explorer.PagingOptions.t/0` used to specify the `:page_size` (defaults to
`#{@default_paging_options.page_size}`) and `:key` (a tuple of the lowest/oldest `{inserted_at, hash}`) and.
Results will be the transactions older than the `inserted_at` and `hash` that are passed.
@ -2438,6 +2504,8 @@ defmodule Explorer.Chain do
|> for_parent_transaction(hash)
|> join_associations(necessity_by_association)
|> where_transaction_has_multiple_internal_transactions()
|> InternalTransaction.where_is_different_from_parent_transaction()
|> InternalTransaction.where_nonpending_block()
|> page_internal_transaction(paging_options)
|> limit(^paging_options.page_size)
|> order_by([internal_transaction], asc: internal_transaction.index)
@ -2463,8 +2531,15 @@ defmodule Explorer.Chain do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
Log
|> join(:inner, [log], transaction in assoc(log, :transaction))
log_with_transactions =
from(log in Log,
inner_join: transaction in Transaction,
on:
transaction.block_hash == log.block_hash and transaction.block_number == log.block_number and
transaction.hash == log.transaction_hash
)
log_with_transactions
|> where([_, transaction], transaction.hash == ^transaction_hash)
|> page_logs(paging_options)
|> limit(^paging_options.page_size)
@ -2495,7 +2570,11 @@ defmodule Explorer.Chain do
TokenTransfer
|> join(:inner, [token_transfer], transaction in assoc(token_transfer, :transaction))
|> where([_, transaction], transaction.hash == ^transaction_hash)
|> where(
[token_transfer, transaction],
transaction.hash == ^transaction_hash and token_transfer.block_hash == transaction.block_hash and
token_transfer.block_number == transaction.block_number
)
|> TokenTransfer.page_token_transfer(paging_options)
|> limit(^paging_options.page_size)
|> order_by([token_transfer], asc: token_transfer.inserted_at)
@ -2530,7 +2609,7 @@ defmodule Explorer.Chain do
def transaction_to_status(%Transaction{status: nil}), do: :awaiting_internal_transactions
def transaction_to_status(%Transaction{status: :ok}), do: :success
def transaction_to_status(%Transaction{status: :error, internal_transactions_indexed_at: nil, error: nil}),
def transaction_to_status(%Transaction{status: :error, error: nil}),
do: {:error, :awaiting_internal_transactions}
def transaction_to_status(%Transaction{status: :error, error: error}) when is_binary(error), do: {:error, error}
@ -2922,7 +3001,7 @@ defmodule Explorer.Chain do
"""
@spec total_supply :: non_neg_integer() | nil
def total_supply do
supply_module().total()
supply_module().total() || 0
end
@doc """
@ -2967,21 +3046,33 @@ defmodule Explorer.Chain do
) :: {:ok, accumulator}
when accumulator: term()
def stream_unfetched_token_instances(initial, reducer) when is_function(reducer, 2) do
nft_tokens =
from(
token in Token,
where: token.type == ^"ERC-721",
select: token.contract_address_hash
)
query =
from(
token_transfer in TokenTransfer,
inner_join: token in Token,
inner_join: token in subquery(nft_tokens),
on: token.contract_address_hash == token_transfer.token_contract_address_hash,
left_join: instance in Instance,
on:
token_transfer.token_id == instance.token_id and
token_transfer.token_contract_address_hash == instance.token_contract_address_hash,
where: token.type == ^"ERC-721" and is_nil(instance.token_id) and not is_nil(token_transfer.token_id),
distinct: [token_transfer.token_contract_address_hash, token_transfer.token_id],
where: is_nil(instance.token_id) and not is_nil(token_transfer.token_id),
select: %{contract_address_hash: token_transfer.token_contract_address_hash, token_id: token_transfer.token_id}
)
Repo.stream_reduce(query, initial, reducer)
distinct_query =
from(
q in subquery(query),
distinct: [q.contract_address_hash, q.token_id]
)
Repo.stream_reduce(distinct_query, initial, reducer)
end
@doc """

@ -7,10 +7,10 @@ defmodule Explorer.Chain.Block do
use Explorer.Schema
alias Explorer.Chain.{Address, Gas, Hash, Transaction}
alias Explorer.Chain.{Address, Gas, Hash, PendingBlockOperation, Transaction}
alias Explorer.Chain.Block.{Reward, SecondDegreeRelation}
@optional_attrs ~w(internal_transactions_indexed_at size refetch_needed total_difficulty difficulty)a
@optional_attrs ~w(size refetch_needed total_difficulty difficulty)a
@required_attrs ~w(consensus gas_limit gas_used hash miner_hash nonce number parent_hash timestamp)a
@ -46,7 +46,6 @@ defmodule Explorer.Chain.Block do
* `timestamp` - When the block was collated
* `total_difficulty` - the total `difficulty` of the chain until this block.
* `transactions` - the `t:Explorer.Chain.Transaction.t/0` in this block.
* `internal_transactions_indexed_at` - when `internal_transactions` were fetched by `Indexer`.
"""
@type t :: %__MODULE__{
consensus: boolean(),
@ -63,7 +62,6 @@ defmodule Explorer.Chain.Block do
timestamp: DateTime.t(),
total_difficulty: difficulty(),
transactions: %Ecto.Association.NotLoaded{} | [Transaction.t()],
internal_transactions_indexed_at: DateTime.t(),
refetch_needed: boolean()
}
@ -78,7 +76,6 @@ defmodule Explorer.Chain.Block do
field(:size, :integer)
field(:timestamp, :utc_datetime_usec)
field(:total_difficulty, :decimal)
field(:internal_transactions_indexed_at, :utc_datetime_usec)
field(:refetch_needed, :boolean)
timestamps()
@ -97,6 +94,8 @@ defmodule Explorer.Chain.Block do
has_many(:transaction_forks, Transaction.Fork, foreign_key: :uncle_hash)
has_many(:rewards, Reward, foreign_key: :block_hash)
has_one(:pending_operations, PendingBlockOperation, foreign_key: :block_hash)
end
def changeset(%__MODULE__{} = block, attrs) do
@ -116,11 +115,23 @@ defmodule Explorer.Chain.Block do
end
def blocks_without_reward_query do
consensus_blocks_query =
from(
b in __MODULE__,
where: b.consensus == true
)
validator_rewards =
from(
r in Reward,
where: r.address_type == ^"validator"
)
from(
b in __MODULE__,
left_join: r in Reward,
b in subquery(consensus_blocks_query),
left_join: r in subquery(validator_rewards),
on: [block_hash: b.hash],
where: is_nil(r.block_hash) and b.consensus == true
where: is_nil(r.block_hash)
)
end

@ -19,7 +19,7 @@ defmodule Explorer.Chain.Block.Range do
to: integer() | :infinity
}
@behaviour Ecto.Type
use Ecto.Type
@doc """
The underlying Postgres type, `int8range`.

@ -68,8 +68,10 @@ defmodule Explorer.Chain.Block.Reward do
Returns a list of tuples representing rewards by the EmissionFunds on POA chains.
The tuples have the format {EmissionFunds, Validator}
"""
@spec fetch_emission_rewards_tuples(Hash.Address.t(), PagingOptions.t()) :: [{t(), t()}]
def fetch_emission_rewards_tuples(address_hash, paging_options) do
def fetch_emission_rewards_tuples(address_hash, paging_options, %{
min_block_number: min_block_number,
max_block_number: max_block_number
}) do
address_rewards =
__MODULE__
|> join_associations()
@ -77,6 +79,7 @@ defmodule Explorer.Chain.Block.Reward do
|> limit(^paging_options.page_size)
|> order_by([_, block], desc: block.number)
|> where([reward], reward.address_hash == ^address_hash)
|> address_rewards_blocks_ranges_clause(min_block_number, max_block_number, paging_options)
|> Repo.all()
case List.first(address_rewards) do
@ -117,4 +120,25 @@ defmodule Explorer.Chain.Block.Reward do
|> join(:inner, [reward], block in assoc(reward, :block))
|> preload(:block)
end
defp address_rewards_blocks_ranges_clause(query, min_block_number, max_block_number, paging_options) do
if is_number(min_block_number) and max_block_number > 0 and min_block_number > 0 do
cond do
paging_options.page_number == 1 ->
query
|> where([_, block], block.number >= ^min_block_number)
min_block_number == max_block_number ->
query
|> where([_, block], block.number == ^min_block_number)
true ->
query
|> where([_, block], block.number >= ^min_block_number)
|> where([_, block], block.number <= ^max_block_number)
end
else
query
end
end
end

@ -3,7 +3,7 @@ defmodule Explorer.Chain.Block.Reward.AddressType do
Block reward address types
"""
@behaviour Ecto.Type
use Ecto.Type
@typedoc """
* `:emission_funds`

@ -20,7 +20,7 @@ defmodule Explorer.Chain.Cache.AddressSum do
# See next `handle_fallback` definition
get_async_task()
{:return, nil}
{:return, Decimal.new(0)}
end
defp handle_fallback(:async_task) do

@ -0,0 +1,53 @@
defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do
@moduledoc """
Cache for address sum minus burnt number.
"""
require Logger
use Explorer.Chain.MapCache,
name: :address_sum_minus_burnt,
key: :sum_minus_burnt,
key: :async_task,
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval],
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl],
callback: &async_task_on_deletion(&1)
alias Explorer.Chain
defp handle_fallback(:sum_minus_burnt) do
# This will get the task PID if one exists and launch a new task if not
# See next `handle_fallback` definition
get_async_task()
{:return, Decimal.new(0)}
end
defp handle_fallback(:async_task) do
# If this gets called it means an async task was requested, but none exists
# so a new one needs to be launched
{:ok, task} =
Task.start(fn ->
try do
result = Chain.fetch_sum_coin_total_supply_minus_burnt()
set_sum_minus_burnt(result)
rescue
e ->
Logger.debug([
"Coudn't update address sum test #{inspect(e)}"
])
end
set_async_task(nil)
end)
{:update, task}
end
# By setting this as a `callback` an async task will be started each time the
# `sum_minus_burnt` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :sum_minus_burnt}), do: get_async_task()
defp async_task_on_deletion(_data), do: nil
end

@ -9,7 +9,7 @@ defmodule Explorer.Chain.Data do
alias Explorer.Chain.Data
alias Poison.Encoder.BitString
@behaviour Ecto.Type
use Ecto.Type
@typedoc """
A variable-byte-length binary, wrapped in a struct, so that it can use protocols.

@ -8,7 +8,7 @@ defmodule Explorer.Chain.Hash.Address do
alias Explorer.Chain.Hash
@behaviour Ecto.Type
use Ecto.Type
@behaviour Hash
@byte_count 20

@ -5,7 +5,7 @@ defmodule Explorer.Chain.Hash.Full do
alias Explorer.Chain.Hash
@behaviour Ecto.Type
use Ecto.Type
@behaviour Hash
@byte_count 32

@ -5,7 +5,7 @@ defmodule Explorer.Chain.Hash.Nonce do
alias Explorer.Chain.Hash
@behaviour Ecto.Type
use Ecto.Type
@behaviour Hash
@byte_count 8

@ -12,7 +12,8 @@ defmodule Explorer.Chain.Import do
Import.Stage.Addresses,
Import.Stage.AddressReferencing,
Import.Stage.BlockReferencing,
Import.Stage.BlockFollowing
Import.Stage.BlockFollowing,
Import.Stage.BlockPending
]
# in order so that foreign keys are inserted before being referenced

@ -8,7 +8,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
import Ecto.Query, only: [from: 2, subquery: 1]
alias Ecto.{Changeset, Multi, Repo}
alias Explorer.Chain.{Address, Block, Import, InternalTransaction, Log, TokenTransfer, Transaction}
alias Explorer.Chain.{Address, Block, Import, PendingBlockOperation, Transaction}
alias Explorer.Chain.Block.Reward
alias Explorer.Chain.Import.Runner
alias Explorer.Chain.Import.Runner.Address.CurrentTokenBalances
@ -56,6 +56,9 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
# Note, needs to be executed after `lose_consensus` for lock acquisition
insert(repo, changes_list, insert_options)
end)
|> Multi.run(:new_pending_operations, fn repo, %{lose_consensus: nonconsensus_hashes} ->
new_pending_operations(repo, nonconsensus_hashes, hashes, insert_options)
end)
|> Multi.run(:uncle_fetched_block_second_degree_relations, fn repo, _ ->
update_block_second_degree_relations(repo, hashes, %{
timeout:
@ -83,18 +86,9 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
transactions: transactions
})
end)
|> Multi.run(:remove_nonconsensus_logs, fn repo, %{derive_transaction_forks: transactions} ->
remove_nonconsensus_logs(repo, transactions, insert_options)
end)
|> Multi.run(:remove_nonconsensus_internal_transactions, fn repo, %{derive_transaction_forks: transactions} ->
remove_nonconsensus_internal_transactions(repo, transactions, insert_options)
end)
|> Multi.run(:acquire_contract_address_tokens, fn repo, _ ->
acquire_contract_address_tokens(repo, consensus_block_numbers)
end)
|> Multi.run(:remove_nonconsensus_token_transfers, fn repo, %{derive_transaction_forks: transactions} ->
remove_nonconsensus_token_transfers(repo, transactions, insert_options)
end)
|> Multi.run(:delete_address_token_balances, fn repo, _ ->
delete_address_token_balances(repo, consensus_block_numbers, insert_options)
end)
@ -125,7 +119,8 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
query =
from(address_current_token_balance in Address.CurrentTokenBalance,
where: address_current_token_balance.block_number in ^consensus_block_numbers,
select: address_current_token_balance.token_contract_address_hash
select: address_current_token_balance.token_contract_address_hash,
distinct: address_current_token_balance.token_contract_address_hash
)
contract_address_hashes = repo.all(query)
@ -160,7 +155,6 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
gas_used: nil,
cumulative_gas_used: nil,
index: nil,
internal_transactions_indexed_at: nil,
status: nil,
error: nil,
updated_at: ^updated_at
@ -251,7 +245,6 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
difficulty: fragment("EXCLUDED.difficulty"),
gas_limit: fragment("EXCLUDED.gas_limit"),
gas_used: fragment("EXCLUDED.gas_used"),
internal_transactions_indexed_at: fragment("EXCLUDED.internal_transactions_indexed_at"),
miner_hash: fragment("EXCLUDED.miner_hash"),
nonce: fragment("EXCLUDED.nonce"),
number: fragment("EXCLUDED.number"),
@ -270,8 +263,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
fragment("EXCLUDED.miner_hash <> ?", block.miner_hash) or fragment("EXCLUDED.nonce <> ?", block.nonce) or
fragment("EXCLUDED.number <> ?", block.number) or fragment("EXCLUDED.parent_hash <> ?", block.parent_hash) or
fragment("EXCLUDED.size <> ?", block.size) or fragment("EXCLUDED.timestamp <> ?", block.timestamp) or
fragment("EXCLUDED.total_difficulty <> ?", block.total_difficulty) or
fragment("EXCLUDED.internal_transactions_indexed_at <> ?", block.internal_transactions_indexed_at)
fragment("EXCLUDED.total_difficulty <> ?", block.total_difficulty)
)
end
@ -317,92 +309,30 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
{:error, %{exception: postgrex_error, consensus_block_numbers: consensus_block_numbers}}
end
defp remove_nonconsensus_token_transfers(repo, forked_transaction_hashes, %{timeout: timeout}) do
ordered_token_transfers =
from(
token_transfer in TokenTransfer,
where: token_transfer.transaction_hash in ^forked_transaction_hashes,
select: token_transfer.transaction_hash,
# Enforce TokenTransfer ShareLocks order (see docs: sharelocks.md)
order_by: [
token_transfer.transaction_hash,
token_transfer.log_index
],
lock: "FOR UPDATE"
)
query =
from(token_transfer in TokenTransfer,
select: map(token_transfer, [:transaction_hash, :log_index]),
inner_join: ordered_token_transfer in subquery(ordered_token_transfers),
on: ordered_token_transfer.transaction_hash == token_transfer.transaction_hash
)
{_count, deleted_token_transfers} = repo.delete_all(query, timeout: timeout)
{:ok, deleted_token_transfers}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, transactions: forked_transaction_hashes}}
end
defp remove_nonconsensus_internal_transactions(repo, forked_transaction_hashes, %{timeout: timeout}) do
query =
from(
internal_transaction in InternalTransaction,
where: internal_transaction.transaction_hash in ^forked_transaction_hashes,
select: %{transaction_hash: internal_transaction.transaction_hash},
# Enforce InternalTransaction ShareLocks order (see docs: sharelocks.md)
order_by: [
internal_transaction.transaction_hash,
internal_transaction.index
],
lock: "FOR UPDATE"
)
delete_query =
from(
i in InternalTransaction,
join: s in subquery(query),
on: i.transaction_hash == s.transaction_hash,
select: map(i, [:transaction_hash, :index])
)
{_count, deleted_internal_transactions} = repo.delete_all(delete_query, timeout: timeout)
{:ok, deleted_internal_transactions}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, transactions: forked_transaction_hashes}}
end
defp remove_nonconsensus_logs(repo, forked_transaction_hashes, %{timeout: timeout}) do
ordered_logs =
from(
log in Log,
where: log.transaction_hash in ^forked_transaction_hashes,
select: log.transaction_hash,
# Enforce Log ShareLocks order (see docs: sharelocks.md)
order_by: [
log.transaction_hash,
log.index
],
lock: "FOR UPDATE"
)
query =
from(log in Log,
select: map(log, [:transaction_hash, :index]),
inner_join: ordered_log in subquery(ordered_logs),
on: ordered_log.transaction_hash == log.transaction_hash
defp new_pending_operations(repo, nonconsensus_hashes, hashes, %{timeout: timeout, timestamps: timestamps}) do
if Application.get_env(:explorer, :json_rpc_named_arguments)[:variant] == EthereumJSONRPC.RSK do
{:ok, []}
else
sorted_pending_ops =
nonconsensus_hashes
|> MapSet.new()
|> MapSet.union(MapSet.new(hashes))
|> Enum.sort()
|> Enum.map(fn hash ->
%{block_hash: hash, fetch_internal_transactions: true}
end)
Import.insert_changes_list(
repo,
sorted_pending_ops,
conflict_target: :block_hash,
on_conflict: PendingBlockOperation.default_on_conflict(),
for: PendingBlockOperation,
returning: true,
timeout: timeout,
timestamps: timestamps
)
{_count, deleted_logs} = repo.delete_all(query, timeout: timeout)
{:ok, deleted_logs}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, transactions: forked_transaction_hashes}}
end
end
defp delete_address_token_balances(_, [], _), do: {:ok, []}

@ -6,20 +6,19 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
require Ecto.Query
require Logger
alias Ecto.Adapters.SQL
alias Ecto.{Changeset, Multi, Repo}
alias Explorer.Chain.{Block, Hash, Import, InternalTransaction, Transaction}
alias Explorer.Chain.{Block, Hash, Import, InternalTransaction, PendingBlockOperation, Transaction}
alias Explorer.Chain.Import.Runner
import Ecto.Query, only: [from: 2]
import Ecto.Query, only: [from: 2, or_where: 3]
@behaviour Runner
# milliseconds
@timeout 60_000
@type imported :: [
%{required(:index) => non_neg_integer(), required(:transaction_hash) => Hash.Full.t()}
]
@type imported :: [InternalTransaction.t()]
@impl Runner
def ecto_schema_module, do: InternalTransaction
@ -48,54 +47,73 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
update_transactions_options = %{timeout: transactions_timeout, timestamps: timestamps}
# filter out params with just `block_number` (indicating blocks without internal transactions)
internal_transactions_params = Enum.filter(changes_list, &Map.has_key?(&1, :type))
# Enforce ShareLocks tables order (see docs: sharelocks.md)
multi
|> Multi.run(:acquire_transactions, fn repo, _ ->
acquire_transactions(repo, changes_list)
|> Multi.run(:acquire_blocks, fn repo, _ ->
acquire_blocks(repo, changes_list)
end)
|> Multi.run(:internal_transactions, fn repo, %{acquire_transactions: transactions} ->
insert(repo, changes_list, transactions, insert_options)
|> Multi.run(:acquire_pending_internal_txs, fn repo, %{acquire_blocks: block_hashes} ->
acquire_pending_internal_txs(repo, block_hashes)
end)
|> Multi.run(:internal_transactions_indexed_at_transactions, fn repo, %{acquire_transactions: transactions} ->
update_transactions(repo, transactions, update_transactions_options)
|> Multi.run(:acquire_transactions, fn repo, %{acquire_pending_internal_txs: pending_block_hashes} ->
acquire_transactions(repo, pending_block_hashes)
end)
|> Multi.run(:invalid_block_numbers, fn _, %{acquire_transactions: transactions} ->
invalid_block_numbers(transactions, internal_transactions_params)
end)
|> Multi.run(:valid_internal_transactions, fn _,
%{
acquire_transactions: transactions,
invalid_block_numbers: invalid_block_numbers
} ->
valid_internal_transactions(transactions, internal_transactions_params, invalid_block_numbers)
end)
|> Multi.run(:remove_left_over_internal_transactions, fn repo,
%{valid_internal_transactions: valid_internal_transactions} ->
remove_left_over_internal_transactions(repo, valid_internal_transactions)
end)
|> Multi.run(:internal_transactions, fn repo, %{valid_internal_transactions: valid_internal_transactions} ->
insert(repo, valid_internal_transactions, insert_options)
end)
|> Multi.run(:update_transactions, fn repo, %{valid_internal_transactions: valid_internal_transactions} ->
update_transactions(repo, valid_internal_transactions, update_transactions_options)
end)
|> Multi.run(:remove_consensus_of_invalid_blocks, fn repo, %{invalid_block_numbers: invalid_block_numbers} ->
remove_consensus_of_invalid_blocks(repo, invalid_block_numbers)
end)
|> Multi.run(:update_pending_blocks_status, fn repo,
%{
acquire_pending_internal_txs: pending_block_hashes,
remove_consensus_of_invalid_blocks: invalid_block_hashes
} ->
update_pending_blocks_status(repo, pending_block_hashes, invalid_block_hashes)
end)
|> Multi.run(
:remove_consensus_of_missing_transactions_blocks,
fn repo, %{internal_transactions: inserted} = results_map ->
# NOTE: for this to work it has to follow the runner `internal_transactions_indexed_at_blocks`
block_hashes = Map.get(results_map, :internal_transactions_indexed_at_blocks, [])
remove_consensus_of_missing_transactions_blocks(repo, block_hashes, changes_list, inserted)
end
)
end
@impl Runner
def timeout, do: @timeout
@spec insert(Repo.t(), [map], [Transaction.t()], %{
@spec insert(Repo.t(), [map], %{
optional(:on_conflict) => Runner.on_conflict(),
required(:timeout) => timeout,
required(:timestamps) => Import.timestamps()
}) ::
{:ok, [%{index: non_neg_integer, transaction_hash: Hash.t()}]}
| {:error, [Changeset.t()]}
defp insert(repo, changes_list, transactions, %{timeout: timeout, timestamps: timestamps} = options)
when is_list(changes_list) do
defp insert(repo, valid_internal_transactions, %{timeout: timeout, timestamps: timestamps} = options)
when is_list(valid_internal_transactions) do
on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0)
transactions_map = Map.new(transactions, &{&1.hash, &1})
final_changes_list =
changes_list
# Enforce InternalTransaction ShareLocks order (see docs: sharelocks.md)
|> Enum.sort_by(&{&1.transaction_hash, &1.index})
|> reject_missing_transactions(transactions_map)
ordered_changes_list = Enum.sort_by(valid_internal_transactions, &{&1.transaction_hash, &1.index})
{:ok, internal_transactions} =
Import.insert_changes_list(
repo,
final_changes_list,
conflict_target: [:transaction_hash, :index],
ordered_changes_list,
conflict_target: [:block_hash, :block_index],
for: InternalTransaction,
on_conflict: on_conflict,
returning: true,
@ -119,24 +137,28 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
from_address_hash: fragment("EXCLUDED.from_address_hash"),
gas: fragment("EXCLUDED.gas"),
gas_used: fragment("EXCLUDED.gas_used"),
# Don't update `index` as it is part of the composite primary key and used for the conflict target
index: fragment("EXCLUDED.index"),
init: fragment("EXCLUDED.init"),
input: fragment("EXCLUDED.input"),
output: fragment("EXCLUDED.output"),
to_address_hash: fragment("EXCLUDED.to_address_hash"),
trace_address: fragment("EXCLUDED.trace_address"),
# Don't update `transaction_hash` as it is part of the composite primary key and used for the conflict target
transaction_hash: fragment("EXCLUDED.transaction_hash"),
transaction_index: fragment("EXCLUDED.transaction_index"),
type: fragment("EXCLUDED.type"),
value: fragment("EXCLUDED.value"),
inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", internal_transaction.inserted_at),
updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", internal_transaction.updated_at)
# Don't update `block_hash` as it is used for the conflict target
# Don't update `block_index` as it is used for the conflict target
]
],
# `IS DISTINCT FROM` is used because it allows `NULL` to be equal to itself
where:
fragment(
"(EXCLUDED.call_type, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code, EXCLUDED.error, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_used, EXCLUDED.init, EXCLUDED.input, EXCLUDED.output, EXCLUDED.to_address_hash, EXCLUDED.trace_address, EXCLUDED.transaction_index, EXCLUDED.type, EXCLUDED.value) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"(EXCLUDED.transaction_hash, EXCLUDED.index, EXCLUDED.call_type, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code, EXCLUDED.error, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_used, EXCLUDED.init, EXCLUDED.input, EXCLUDED.output, EXCLUDED.to_address_hash, EXCLUDED.trace_address, EXCLUDED.transaction_index, EXCLUDED.type, EXCLUDED.value) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
internal_transaction.transaction_hash,
internal_transaction.index,
internal_transaction.call_type,
internal_transaction.created_contract_address_hash,
internal_transaction.created_contract_code,
@ -156,18 +178,42 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
)
end
defp acquire_transactions(repo, internal_transactions) do
transaction_hashes =
internal_transactions
|> MapSet.new(& &1.transaction_hash)
|> MapSet.to_list()
defp acquire_blocks(repo, changes_list) do
block_numbers = Enum.map(changes_list, & &1.block_number)
query =
from(
b in Block,
where: b.number in ^block_numbers and b.consensus,
select: b.hash,
# Enforce Block ShareLocks order (see docs: sharelocks.md)
order_by: [asc: b.hash],
lock: "FOR UPDATE"
)
{:ok, repo.all(query)}
end
defp acquire_pending_internal_txs(repo, block_hashes) do
query =
from(
pending_ops in PendingBlockOperation,
where: pending_ops.block_hash in ^block_hashes,
where: pending_ops.fetch_internal_transactions,
select: pending_ops.block_hash,
# Enforce PendingBlockOperation ShareLocks order (see docs: sharelocks.md)
order_by: [asc: pending_ops.block_hash],
lock: "FOR UPDATE"
)
{:ok, repo.all(query)}
end
defp acquire_transactions(repo, pending_block_hashes) do
query =
from(
t in Transaction,
where: t.hash in ^transaction_hashes,
# do not consider pending transactions
where: not is_nil(t.block_hash),
where: t.block_hash in ^pending_block_hashes,
select: map(t, [:hash, :block_hash, :block_number]),
# Enforce Transaction ShareLocks order (see docs: sharelocks.md)
order_by: t.hash,
@ -177,22 +223,115 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
{:ok, repo.all(query)}
end
defp update_transactions(repo, transactions, %{
defp invalid_block_numbers(transactions, internal_transactions_params) do
# Finds all mistmatches between transactions and internal transactions
# for a block number:
# - there are no internal txs for some transactions
# - there are no transactions for some internal transactions
# - there are internal txs with a different block number than their transactions
# Returns block numbers where any of these issues is found
required_tuples = MapSet.new(transactions, &{&1.hash, &1.block_number})
candidate_tuples = MapSet.new(internal_transactions_params, &{&1.transaction_hash, &1.block_number})
all_tuples = MapSet.union(required_tuples, candidate_tuples)
common_tuples = MapSet.intersection(required_tuples, candidate_tuples)
invalid_numbers =
all_tuples
|> MapSet.difference(common_tuples)
|> MapSet.new(fn {_hash, block_number} -> block_number end)
|> MapSet.to_list()
{:ok, invalid_numbers}
end
defp valid_internal_transactions(transactions, internal_transactions_params, invalid_block_numbers) do
blocks_map = Map.new(transactions, &{&1.block_number, &1.block_hash})
valid_internal_txs =
internal_transactions_params
|> Enum.group_by(& &1.block_number)
|> Map.drop(invalid_block_numbers)
|> Enum.flat_map(fn {block_number, entries} ->
block_hash = Map.fetch!(blocks_map, block_number)
entries
|> Enum.sort_by(&{&1.transaction_hash, &1.index})
|> Enum.with_index()
|> Enum.map(fn {entry, index} ->
entry
|> Map.put(:block_hash, block_hash)
|> Map.put(:block_index, index)
end)
end)
{:ok, valid_internal_txs}
end
def defer_internal_transactions_primary_key(repo) do
# Allows internal_transactions primary key to not be checked during the
# DB transactions and instead be checked only at the end of it.
# This allows us to use a more efficient upserting logic, while keeping the
# uniqueness valid.
SQL.query(repo, "SET CONSTRAINTS internal_transactions_pkey DEFERRED")
end
def remove_left_over_internal_transactions(repo, valid_internal_transactions) do
# Removes internal transactions that were part of a block before a refetch
# and have not been upserted with new ones (if any exist).
case valid_internal_transactions do
[] ->
{:ok, []}
_ ->
try do
delete_query_for_block_hash_block_index =
valid_internal_transactions
|> Enum.group_by(& &1.block_hash, & &1.block_index)
|> Enum.map(fn {block_hash, indexes} -> {block_hash, Enum.max(indexes)} end)
|> Enum.reduce(InternalTransaction, fn {block_hash, max_index}, acc ->
or_where(acc, [it], it.block_hash == ^block_hash and it.block_index > ^max_index)
end)
# removes old recoreds with the same primary key (transaction hash, transaction index)
delete_query =
valid_internal_transactions
|> Enum.map(fn params -> {params.transaction_hash, params.index} end)
|> Enum.reduce(delete_query_for_block_hash_block_index, fn {transaction_hash, index}, acc ->
or_where(acc, [it], it.transaction_hash == ^transaction_hash and it.index == ^index)
end)
# ShareLocks order already enforced by `acquire_pending_internal_txs` (see docs: sharelocks.md)
{count, result} = repo.delete_all(delete_query, [])
{:ok, {count, result}}
rescue
postgrex_error in Postgrex.Error -> {:error, %{exception: postgrex_error}}
end
end
end
defp update_transactions(repo, valid_internal_transactions, %{
timeout: timeout,
timestamps: timestamps
})
when is_list(transactions) do
transaction_hashes = Enum.map(transactions, & &1.hash)
when is_list(valid_internal_transactions) do
transaction_hashes =
valid_internal_transactions
|> MapSet.new(& &1.transaction_hash)
|> MapSet.to_list()
update_query =
from(
t in Transaction,
# pending transactions are already excluded by `acquire_transactions`
where: t.hash in ^transaction_hashes,
# ShareLocks order already enforced by `acquire_transactions` (see docs: sharelocks.md)
update: [
set: [
internal_transactions_indexed_at: ^timestamps.updated_at,
created_contract_address_hash:
fragment(
"(SELECT it.created_contract_address_hash FROM internal_transactions AS it WHERE it.transaction_hash = ? ORDER BY it.index ASC LIMIT 1)",
@ -209,7 +348,8 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
t.hash,
type(^:ok, t.status),
type(^:error, t.status)
)
),
updated_at: ^timestamps.updated_at
]
]
)
@ -224,26 +364,14 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
end
end
# If not using Parity this is not relevant
defp remove_consensus_of_missing_transactions_blocks(_, [], _, _), do: {:ok, []}
defp remove_consensus_of_missing_transactions_blocks(repo, block_hashes, changes_list, inserted) do
inserted_block_numbers = MapSet.new(inserted, & &1.block_number)
missing_transactions_block_numbers =
changes_list
|> MapSet.new(& &1.block_number)
|> MapSet.difference(inserted_block_numbers)
|> MapSet.to_list()
defp remove_consensus_of_invalid_blocks(repo, invalid_block_numbers) do
update_query =
from(
b in Block,
where: b.number in ^missing_transactions_block_numbers,
where: b.hash in ^block_hashes,
select: b.number,
# ShareLocks order already enforced by `internal_transactions_indexed_at_blocks` (see docs: sharelocks.md)
update: [set: [consensus: false, internal_transactions_indexed_at: nil]]
where: b.number in ^invalid_block_numbers and b.consensus,
select: b.hash,
# ShareLocks order already enforced by `acquire_blocks` (see docs: sharelocks.md)
update: [set: [consensus: false]]
)
try do
@ -252,24 +380,39 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
Logger.debug(fn ->
[
"consensus removed from blocks with numbers: ",
inspect(missing_transactions_block_numbers),
" because of missing transactions"
inspect(invalid_block_numbers),
" because of mismatching transactions"
]
end)
{:ok, result}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, missing_transactions_block_numbers: missing_transactions_block_numbers}}
{:error, %{exception: postgrex_error, invalid_block_numbers: invalid_block_numbers}}
end
end
defp reject_missing_transactions(ordered_changes_list, transactions_map) do
Enum.reject(ordered_changes_list, fn %{transaction_hash: hash} ->
transactions_map
|> Map.get(hash, %{})
|> Map.get(:block_hash)
|> is_nil()
end)
def update_pending_blocks_status(repo, pending_hashes, invalid_block_hashes) do
valid_block_hashes =
pending_hashes
|> MapSet.new()
|> MapSet.difference(MapSet.new(invalid_block_hashes))
|> MapSet.to_list()
delete_query =
from(
pending_ops in PendingBlockOperation,
where: pending_ops.block_hash in ^valid_block_hashes
)
try do
# ShreLocks order already enforced by `acquire_pending_internal_txs` (see docs: sharelocks.md)
{_count, deleted} = repo.delete_all(delete_query, [])
{:ok, deleted}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, pending_hashes: valid_block_hashes}}
end
end
end

@ -1,82 +0,0 @@
defmodule Explorer.Chain.Import.Runner.InternalTransactionsIndexedAtBlocks do
@moduledoc """
Bulk updates `internal_transactions_indexed_at` for provided blocks
"""
require Ecto.Query
alias Ecto.Multi
alias Explorer.Chain.Block
alias Explorer.Chain.Import.Runner
import Ecto.Query, only: [from: 2]
@behaviour Runner
# milliseconds
@timeout 60_000
@type imported :: [%{number: Block.block_number()}]
@impl Runner
def ecto_schema_module, do: Block
@impl Runner
def option_key, do: :internal_transactions_indexed_at_blocks
@impl Runner
def imported_table_row do
%{
value_type: "[%{number: Explorer.Chain.Block.block_number()}]",
value_description: "List of block numbers to set `internal_transactions_indexed_at` field for"
}
end
@impl Runner
def run(multi, changes_list, %{timestamps: timestamps} = options) when is_map(options) do
transactions_timeout = options[Runner.Transactions.option_key()][:timeout] || Runner.Transactions.timeout()
update_transactions_options = %{timeout: transactions_timeout, timestamps: timestamps}
multi
|> Multi.run(:internal_transactions_indexed_at_blocks, fn repo, _ ->
update_blocks(repo, changes_list, update_transactions_options)
end)
end
@impl Runner
def timeout, do: @timeout
defp update_blocks(_repo, [], %{}), do: {:ok, []}
defp update_blocks(repo, changes_list, %{
timeout: timeout,
timestamps: timestamps
})
when is_list(changes_list) do
block_numbers = Enum.map(changes_list, fn %{number: number} -> number end)
query =
from(
b in Block,
where: b.number in ^block_numbers and b.consensus,
# Enforce Block ShareLocks order (see docs: sharelocks.md)
order_by: [asc: b.hash],
lock: "FOR UPDATE"
)
try do
{_, result} =
repo.update_all(
from(b in Block, join: s in subquery(query), on: b.hash == s.hash, select: b.hash),
[set: [internal_transactions_indexed_at: timestamps.updated_at]],
timeout: timeout
)
{:ok, result}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, block_numbers: block_numbers}}
end
end
end

@ -59,13 +59,13 @@ defmodule Explorer.Chain.Import.Runner.Logs do
on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0)
# Enforce Log ShareLocks order (see docs: sharelocks.md)
ordered_changes_list = Enum.sort_by(changes_list, &{&1.transaction_hash, &1.index})
ordered_changes_list = Enum.sort_by(changes_list, &{&1.transaction_hash, &1.block_hash, &1.index})
{:ok, _} =
Import.insert_changes_list(
repo,
ordered_changes_list,
conflict_target: [:transaction_hash, :index],
conflict_target: [:transaction_hash, :index, :block_hash],
on_conflict: on_conflict,
for: Log,
returning: true,

@ -154,7 +154,7 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do
total = repo.one!(total_query)
if total > Decimal.new(0) do
if total.value > Decimal.new(0) do
update_query =
from(
p in StakingPool,
@ -162,8 +162,8 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do
# ShareLocks order already enforced by `acquire_all_staking_pools` (see docs: sharelocks.md)
update: [
set: [
staked_ratio: p.staked_amount / ^total * 100,
likelihood: p.staked_amount / ^total * 100
staked_ratio: p.staked_amount / ^total.value * 100,
likelihood: p.staked_amount / ^total.value * 100
]
]
)

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

Loading…
Cancel
Save