Merge branch 'master' into ab-remove-duplicate-indexes

pull/2517/head
Victor Baranov 5 years ago committed by GitHub
commit 39a9636ad9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      CHANGELOG.md
  2. 3
      PULL_REQUEST_TEMPLATE.md
  3. 4
      apps/block_scout_web/assets/__tests__/lib/async_listing_load.js
  4. 5
      apps/block_scout_web/assets/css/theme/_dark-theme.scss
  5. 21
      apps/block_scout_web/assets/js/lib/async_listing_load.js
  6. 10
      apps/block_scout_web/config/config.exs
  7. 3
      apps/block_scout_web/lib/block_scout_web.ex
  8. 73
      apps/block_scout_web/lib/block_scout_web/api_router.ex
  9. 7
      apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_by_day_controller.ex
  10. 15
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex
  11. 2
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  12. 266
      apps/block_scout_web/lib/block_scout_web/router.ex
  13. 17
      apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex
  14. 6
      apps/block_scout_web/lib/block_scout_web/templates/common_components/_tile-loader.html.eex
  15. 2
      apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex
  16. 218
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  17. 24
      apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex
  18. 1
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex
  19. 29
      apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
  20. 43
      apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
  21. 204
      apps/block_scout_web/lib/block_scout_web/web_router.ex
  22. 4
      apps/block_scout_web/mix.exs
  23. 72
      apps/block_scout_web/priv/gettext/default.pot
  24. 72
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  25. 5
      apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs
  26. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_contract_controller_test.exs
  27. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs
  28. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_token_controller_test.exs
  29. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_token_transfer_controller_test.exs
  30. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs
  31. 8
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/rpc_translator_test.exs
  32. 1
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs
  33. 4
      apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs
  34. 4
      apps/block_scout_web/test/block_scout_web/controllers/api/v1/health_controller_test.exs
  35. 4
      apps/block_scout_web/test/block_scout_web/controllers/api/v1/supply_controller_test.exs
  36. 4
      apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs
  37. 2
      apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs
  38. 2
      apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs
  39. 2
      apps/block_scout_web/test/block_scout_web/controllers/pending_transaction_controller_test.exs
  40. 2
      apps/block_scout_web/test/block_scout_web/controllers/recent_transactions_controller_test.exs
  41. 2
      apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs
  42. 2
      apps/block_scout_web/test/block_scout_web/controllers/transaction_internal_transaction_controller_test.exs
  43. 2
      apps/block_scout_web/test/block_scout_web/controllers/transaction_log_controller_test.exs
  44. 2
      apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs
  45. 2
      apps/block_scout_web/test/block_scout_web/features/pages/transaction_logs_page.ex
  46. 14
      apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs
  47. 2
      apps/block_scout_web/test/support/conn_case.ex
  48. 4
      apps/explorer/config/config.exs
  49. 28
      apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
  50. 4
      apps/explorer/mix.exs
  51. 24
      apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs
  52. 2
      apps/indexer/config/config.exs
  53. 12
      apps/indexer/lib/indexer/application.ex
  54. 6
      docs/env-variables.md
  55. 6
      mix.lock

@ -1,12 +1,22 @@
## Current ## Current
### Features ### Features
- [#2376](https://github.com/poanetwork/blockscout/pull/2376) - Split API and WebApp routes
- [#2477](https://github.com/poanetwork/blockscout/pull/2477) - aggregate token transfers on transaction page - [#2477](https://github.com/poanetwork/blockscout/pull/2477) - aggregate token transfers on transaction page
- [#2458](https://github.com/poanetwork/blockscout/pull/2458) - Add LAST_BLOCK var to add ability indexing in the range of blocks - [#2458](https://github.com/poanetwork/blockscout/pull/2458) - Add LAST_BLOCK var to add ability indexing in the range of blocks
- [#2456](https://github.com/poanetwork/blockscout/pull/2456) - fetch pending transactions for geth - [#2456](https://github.com/poanetwork/blockscout/pull/2456) - fetch pending transactions for geth
- [#2403](https://github.com/poanetwork/blockscout/pull/2403) - Return gasPrice field at the result of gettxinfo method
### Fixes ### Fixes
- [#2528](https://github.com/poanetwork/blockscout/pull/2528) - fix coin history chart data
- [#2520](https://github.com/poanetwork/blockscout/pull/2520) - Hide loading message when fetching is failed
- [#2523](https://github.com/poanetwork/blockscout/pull/2523) - Avoid importing internal_transactions of pending transactions
- [#2519](https://github.com/poanetwork/blockscout/pull/2519) - enable `First` page button in pagination
- [#2517](https://github.com/poanetwork/blockscout/pull/2517) - remove duplicate indexes - [#2517](https://github.com/poanetwork/blockscout/pull/2517) - remove duplicate indexes
- [#2515](https://github.com/poanetwork/blockscout/pull/2515) - do not aggregate NFT token transfers
- [#2512](https://github.com/poanetwork/blockscout/pull/2512) - alert link fix
- [#2508](https://github.com/poanetwork/blockscout/pull/2508) - logs view columns fix
- [#2506](https://github.com/poanetwork/blockscout/pull/2506) - fix two active tab in the top menu
- [#2503](https://github.com/poanetwork/blockscout/pull/2503) - Mitigate autocompletion library influence to page loading performance - [#2503](https://github.com/poanetwork/blockscout/pull/2503) - Mitigate autocompletion library influence to page loading performance
- [#2502](https://github.com/poanetwork/blockscout/pull/2502) - increase reward task timeout - [#2502](https://github.com/poanetwork/blockscout/pull/2502) - increase reward task timeout
- [#2463](https://github.com/poanetwork/blockscout/pull/2463) - dark theme fixes - [#2463](https://github.com/poanetwork/blockscout/pull/2463) - dark theme fixes
@ -16,6 +26,8 @@
- [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB - [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB
### Chore ### Chore
- [#2507](https://github.com/poanetwork/blockscout/pull/2507) - update minor version of ecto, ex_machina, phoenix_live_reload
- [#2516](https://github.com/poanetwork/blockscout/pull/2516) - update absinthe plug from fork
- [#2473](https://github.com/poanetwork/blockscout/pull/2473) - get rid of cldr warnings - [#2473](https://github.com/poanetwork/blockscout/pull/2473) - get rid of cldr warnings
- [#2402](https://github.com/poanetwork/blockscout/pull/2402) - bump otp version to 22.0 - [#2402](https://github.com/poanetwork/blockscout/pull/2402) - bump otp version to 22.0
- [#2492](https://github.com/poanetwork/blockscout/pull/2492) - hide decoded row if event is not decoded - [#2492](https://github.com/poanetwork/blockscout/pull/2492) - hide decoded row if event is not decoded

@ -35,4 +35,5 @@
- [ ] If I added new functionality, I added tests covering it. - [ ] If I added new functionality, I added tests covering it.
- [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again. - [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again.
- [ ] I checked whether I should update the docs and did so if necessary - [ ] I checked whether I should update the docs and did so if necessary
- [ ] If I added/changed/removed ENV var, I should update the list of env vars in https://github.com/poanetwork/blockscout/blob/master/docs/env-variables.md to reflect changes in the table here https://poanetwork.github.io/blockscout/#/env-variables?id=blockscout-env-variables - [ ] If I added/changed/removed ENV var, I should update the list of env vars in https://github.com/poanetwork/blockscout/blob/master/docs/env-variables.md to reflect changes in the table here https://poanetwork.github.io/blockscout/#/env-variables?id=blockscout-env-variables. I've set `master` in the `Version` column.
- [ ] If I add new indices into DB, I checked, that they don't redundant with PGHero or other tools

@ -46,14 +46,12 @@ describe('REQUEST_ERROR', () => {
describe('FINISH_REQUEST', () => { describe('FINISH_REQUEST', () => {
test('sets loading status to false', () => { test('sets loading status to false', () => {
const state = Object.assign({}, asyncInitialState, { const state = Object.assign({}, asyncInitialState, {
loading: true, loading: true
loadingFirstPage: true
}) })
const action = { type: 'FINISH_REQUEST' } const action = { type: 'FINISH_REQUEST' }
const output = asyncReducer(state, action) const output = asyncReducer(state, action)
expect(output.loading).toEqual(false) expect(output.loading).toEqual(false)
expect(output.loadingFirstPage).toEqual(false)
}) })
}) })

@ -680,4 +680,9 @@ $labels-dark: #8a8dba; // header nav, labels
color: #3f436b !important; color: #3f436b !important;
border-right-color: #3f436b !important; border-right-color: #3f436b !important;
} }
// alert link
.alert-link {
color: $dark-secondary;
}
} }

@ -51,8 +51,6 @@ export const asyncInitialState = {
requestError: false, requestError: false,
/* if response has no items */ /* if response has no items */
emptyResponse: false, emptyResponse: false,
/* if it is loading the first page */
loadingFirstPage: true,
/* link to the next page */ /* link to the next page */
nextPagePath: null, nextPagePath: null,
/* link to the previous page */ /* link to the previous page */
@ -80,8 +78,7 @@ export function asyncReducer (state = asyncInitialState, action) {
} }
case 'FINISH_REQUEST': { case 'FINISH_REQUEST': {
return Object.assign({}, state, { return Object.assign({}, state, {
loading: false, loading: false
loadingFirstPage: false
}) })
} }
case 'ITEMS_FETCHED': { case 'ITEMS_FETCHED': {
@ -134,7 +131,7 @@ export const elements = {
}, },
'[data-async-listing] [data-loading-message]': { '[data-async-listing] [data-loading-message]': {
render ($el, state) { render ($el, state) {
if (state.loadingFirstPage) return $el.show() if (state.loading) return $el.show()
$el.hide() $el.hide()
} }
@ -143,7 +140,7 @@ export const elements = {
render ($el, state) { render ($el, state) {
if ( if (
!state.requestError && !state.requestError &&
(!state.loading || !state.loadingFirstPage) && (!state.loading) &&
state.items.length === 0 state.items.length === 0
) { ) {
return $el.show() return $el.show()
@ -201,6 +198,16 @@ export const elements = {
$el.attr('href', state.prevPagePath) $el.attr('href', state.prevPagePath)
} }
}, },
'[data-async-listing] [data-first-page-button]': {
render ($el, state) {
if (state.pagesStack.length === 0) {
return $el.hide()
}
$el.show()
$el.attr('disabled', false)
$el.attr('href', window.location.href.split('?')[0])
}
},
'[data-async-listing] [data-page-number]': { '[data-async-listing] [data-page-number]': {
render ($el, state) { render ($el, state) {
if (state.emptyResponse) { if (state.emptyResponse) {
@ -216,7 +223,7 @@ export const elements = {
}, },
'[data-async-listing] [data-loading-button]': { '[data-async-listing] [data-loading-button]': {
render ($el, state) { render ($el, state) {
if (!state.loadingFirstPage && state.loading) return $el.show() if (state.loading) return $el.show()
$el.hide() $el.hide()
} }

@ -28,7 +28,9 @@ config :block_scout_web,
"EtherChain" => "https://www.etherchain.org/", "EtherChain" => "https://www.etherchain.org/",
"Bloxy" => "https://bloxy.info/" "Bloxy" => "https://bloxy.info/"
}, },
other_networks: System.get_env("SUPPORTED_CHAINS") other_networks: System.get_env("SUPPORTED_CHAINS"),
webapp_url: System.get_env("WEBAPP_URL"),
api_url: System.get_env("API_URL")
config :block_scout_web, BlockScoutWeb.Counters.BlocksIndexedCounter, enabled: true config :block_scout_web, BlockScoutWeb.Counters.BlocksIndexedCounter, enabled: true
@ -85,6 +87,12 @@ config :wobserver,
discovery: :none, discovery: :none,
mode: :plug mode: :plug
config :block_scout_web, BlockScoutWeb.ApiRouter,
writing_enabled: System.get_env("DISABLE_WRITE_API") != "true",
reading_enabled: System.get_env("DISABLE_READ_API") != "true"
config :block_scout_web, BlockScoutWeb.WebRouter, enabled: System.get_env("DISABLE_WEBAPP") != "true"
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

@ -24,6 +24,7 @@ defmodule BlockScoutWeb do
import BlockScoutWeb.Controller import BlockScoutWeb.Controller
import BlockScoutWeb.Router.Helpers import BlockScoutWeb.Router.Helpers
import BlockScoutWeb.WebRouter.Helpers, except: [static_path: 2]
import BlockScoutWeb.Gettext import BlockScoutWeb.Gettext
import BlockScoutWeb.ErrorHelpers import BlockScoutWeb.ErrorHelpers
import Plug.Conn import Plug.Conn
@ -55,6 +56,8 @@ defmodule BlockScoutWeb do
WeiHelpers WeiHelpers
} }
import BlockScoutWeb.WebRouter.Helpers, except: [static_path: 2]
import PhoenixFormAwesomplete import PhoenixFormAwesomplete
end end
end end

@ -0,0 +1,73 @@
defmodule RPCTranslatorForwarder do
@moduledoc """
Phoenix router limits forwarding,
so this module is to forward old paths for backward compatibility
"""
alias BlockScoutWeb.API.RPC.RPCTranslator
defdelegate init(opts), to: RPCTranslator
defdelegate call(conn, opts), to: RPCTranslator
end
defmodule BlockScoutWeb.ApiRouter do
@moduledoc """
Router for API
"""
use BlockScoutWeb, :router
pipeline :api do
plug(:accepts, ["json"])
end
scope "/v1", BlockScoutWeb.API.V1, as: :api_v1 do
pipe_through(:api)
get("/health", HealthController, :health)
if Application.get_env(:block_scout_web, __MODULE__)[:writing_enabled] do
post("/decompiled_smart_contract", DecompiledSmartContractController, :create)
post("/verified_smart_contracts", VerifiedSmartContractController, :create)
end
end
if Application.get_env(:block_scout_web, __MODULE__)[:reading_enabled] do
scope "/" do
alias BlockScoutWeb.API.{RPC, V1}
pipe_through(:api)
scope "/v1", as: :api_v1 do
get("/supply", V1.SupplyController, :supply)
post("/eth_rpc", RPC.EthController, :eth_request)
end
# For backward compatibility. Should be removed
post("/eth_rpc", RPC.EthController, :eth_request)
end
end
scope "/" do
pipe_through(:api)
alias BlockScoutWeb.API.RPC
scope "/v1", as: :api_v1 do
forward("/", RPC.RPCTranslator, %{
"block" => {RPC.BlockController, []},
"account" => {RPC.AddressController, []},
"logs" => {RPC.LogsController, []},
"token" => {RPC.TokenController, []},
"stats" => {RPC.StatsController, []},
"contract" => {RPC.ContractController, [:verify]},
"transaction" => {RPC.TransactionController, []}
})
end
# For backward compatibility. Should be removed
forward("/", RPCTranslatorForwarder, %{
"block" => {RPC.BlockController, []},
"account" => {RPC.AddressController, []},
"logs" => {RPC.LogsController, []},
"token" => {RPC.TokenController, []},
"stats" => {RPC.StatsController, []},
"contract" => {RPC.ContractController, [:verify]},
"transaction" => {RPC.TransactionController, []}
})
end
end

@ -9,7 +9,12 @@ defmodule BlockScoutWeb.AddressCoinBalanceByDayController do
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"}) do def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do
balances_by_day = Chain.address_to_balances_by_day(address_hash) balances_by_day =
address_hash
|> Chain.address_to_balances_by_day()
|> Enum.map(fn %{value: value} = map ->
Map.put(map, :value, Decimal.to_float(value))
end)
json(conn, balances_by_day) json(conn, balances_by_day)
end end

@ -25,8 +25,9 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
def init(opts), do: opts def init(opts), do: opts
def call(%Conn{params: %{"module" => module, "action" => action}} = conn, translations) do def call(%Conn{params: %{"module" => module, "action" => action}} = conn, translations) do
with {:ok, controller} <- translate_module(translations, module), with {:ok, {controller, write_actions}} <- translate_module(translations, module),
{:ok, action} <- translate_action(action), {:ok, action} <- translate_action(action),
true <- action_accessed?(action, write_actions),
{:ok, conn} <- call_controller(conn, controller, action) do {:ok, conn} <- call_controller(conn, controller, action) do
conn conn
else else
@ -64,7 +65,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
end end
@doc false @doc false
@spec translate_module(map(), String.t()) :: {:ok, module()} | {:error, :no_action} @spec translate_module(map(), String.t()) :: {:ok, {module(), list(atom())}} | {:error, :no_action}
defp translate_module(translations, module) do defp translate_module(translations, module) do
module_lowercase = String.downcase(module) module_lowercase = String.downcase(module)
@ -83,6 +84,16 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
ArgumentError -> {:error, :no_action} ArgumentError -> {:error, :no_action}
end end
defp action_accessed?(action, write_actions) do
conf = Application.get_env(:block_scout_web, BlockScoutWeb.ApiRouter)
if action in write_actions do
conf[:writing_enabled] || {:error, :no_action}
else
conf[:reading_enabled] || {:error, :no_action}
end
end
@doc false @doc false
@spec call_controller(Conn.t(), module(), atom()) :: {:ok, Conn.t()} | {:error, :no_action} | {:error, Exception.t()} @spec call_controller(Conn.t(), module(), atom()) :: {:ok, Conn.t()} | {:error, :no_action} | {:error, Exception.t()}
defp call_controller(conn, controller, action) do defp call_controller(conn, controller, action) do

@ -440,6 +440,7 @@ defmodule BlockScoutWeb.Etherscan do
"from" => "0x000000000000000000000000000000000000000c", "from" => "0x000000000000000000000000000000000000000c",
"gasLimit" => "91966", "gasLimit" => "91966",
"gasUsed" => "95123", "gasUsed" => "95123",
"gasPrice" => "100000",
"hash" => "0x0000000000000000000000000000000000000000000000000000000000000004", "hash" => "0x0000000000000000000000000000000000000000000000000000000000000004",
"input" => "0x04", "input" => "0x04",
"logs" => [ "logs" => [
@ -986,6 +987,7 @@ defmodule BlockScoutWeb.Etherscan do
input: @input_type, input: @input_type,
gasLimit: @wei_type, gasLimit: @wei_type,
gasUsed: @gas_type, gasUsed: @gas_type,
gasPrice: @wei_type,
logs: %{ logs: %{
type: "array", type: "array",
array_type: @logs_details array_type: @logs_details

@ -2,6 +2,7 @@ defmodule BlockScoutWeb.Router do
use BlockScoutWeb, :router use BlockScoutWeb, :router
alias BlockScoutWeb.Plug.GraphQL alias BlockScoutWeb.Plug.GraphQL
alias BlockScoutWeb.{ApiRouter, WebRouter}
forward("/wobserver", Wobserver.Web.Router) forward("/wobserver", Wobserver.Web.Router)
forward("/admin", BlockScoutWeb.AdminRouter) forward("/admin", BlockScoutWeb.AdminRouter)
@ -18,249 +19,54 @@ defmodule BlockScoutWeb.Router do
plug(:accepts, ["json"]) plug(:accepts, ["json"])
end end
scope "/api/v1", BlockScoutWeb.API.V1, as: :api_v1 do forward("/api", ApiRouter)
pipe_through(:api)
get("/supply", SupplyController, :supply) if Application.get_env(:block_scout_web, ApiRouter)[:reading_enabled] do
# Needs to be 200 to support the schema introspection for graphiql
@max_complexity 200
get("/health", HealthController, :health) forward("/graphql", Absinthe.Plug,
schema: BlockScoutWeb.Schema,
analyze_complexity: true,
max_complexity: @max_complexity
)
resources("/decompiled_smart_contract", DecompiledSmartContractController, only: [:create]) forward("/graphiql", Absinthe.Plug.GraphiQL,
resources("/verified_smart_contracts", VerifiedSmartContractController, only: [:create]) schema: BlockScoutWeb.Schema,
interface: :advanced,
default_query: GraphQL.default_query(),
socket: BlockScoutWeb.UserSocket,
analyze_complexity: true,
max_complexity: @max_complexity
)
else
scope "/", BlockScoutWeb do
pipe_through(:browser)
get("/api_docs", PageNotFoundController, :index)
get("/eth_rpc_api_docs", PageNotFoundController, :index)
end
end end
scope "/verify_smart_contract" do scope "/", BlockScoutWeb do
pipe_through(:api) pipe_through(:browser)
post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create) get("/api_docs", APIDocsController, :index)
get("/eth_rpc_api_docs", APIDocsController, :eth_rpc)
end end
scope "/api", BlockScoutWeb.API.RPC do scope "/verify_smart_contract" do
pipe_through(:api) pipe_through(:api)
alias BlockScoutWeb.API.RPC post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create)
post("/eth_rpc", EthController, :eth_request)
forward("/", RPCTranslator, %{
"block" => RPC.BlockController,
"account" => RPC.AddressController,
"logs" => RPC.LogsController,
"token" => RPC.TokenController,
"stats" => RPC.StatsController,
"contract" => RPC.ContractController,
"transaction" => RPC.TransactionController
})
end
# Needs to be 200 to support the schema introspection for graphiql
@max_complexity 200
forward("/graphql", Absinthe.Plug,
schema: BlockScoutWeb.Schema,
analyze_complexity: true,
max_complexity: @max_complexity
)
forward("/graphiql", Absinthe.Plug.GraphiQL,
schema: BlockScoutWeb.Schema,
interface: :advanced,
default_query: GraphQL.default_query(),
socket: BlockScoutWeb.UserSocket,
analyze_complexity: true,
max_complexity: @max_complexity
)
# Disallows Iframes (write routes)
scope "/", BlockScoutWeb do
pipe_through(:browser)
end end
# Allows Iframes (read-only routes) if Application.get_env(:block_scout_web, WebRouter)[:enabled] do
scope "/", BlockScoutWeb do forward("/", BlockScoutWeb.WebRouter)
pipe_through([:browser, BlockScoutWeb.Plug.AllowIframe]) else
scope "/", BlockScoutWeb do
resources("/", ChainController, only: [:show], singleton: true, as: :chain) pipe_through(:browser)
resources("/market_history_chart", Chain.MarketHistoryChartController,
only: [:show],
singleton: true
)
resources "/blocks", BlockController, only: [:index, :show], param: "hash_or_number" do forward("/", APIDocsController, :index)
resources("/transactions", BlockTransactionController, only: [:index], as: :transaction)
end end
get("/reorgs", BlockController, :reorg, as: :reorg)
get("/uncles", BlockController, :uncle, as: :uncle)
resources("/pending_transactions", PendingTransactionController, only: [:index])
resources("/recent_transactions", RecentTransactionsController, only: [:index])
get("/txs", TransactionController, :index)
resources "/tx", TransactionController, only: [:show] do
resources(
"/internal_transactions",
TransactionInternalTransactionController,
only: [:index],
as: :internal_transaction
)
resources(
"/raw_trace",
TransactionRawTraceController,
only: [:index],
as: :raw_trace
)
resources("/logs", TransactionLogController, only: [:index], as: :log)
resources("/token_transfers", TransactionTokenTransferController,
only: [:index],
as: :token_transfer
)
end
resources("/accounts", AddressController, only: [:index])
resources "/address", AddressController, only: [:show] do
resources("/transactions", AddressTransactionController, only: [:index], as: :transaction)
resources(
"/internal_transactions",
AddressInternalTransactionController,
only: [:index],
as: :internal_transaction
)
resources(
"/validations",
AddressValidationController,
only: [:index],
as: :validation
)
resources(
"/contracts",
AddressContractController,
only: [:index],
as: :contract
)
resources(
"/decompiled_contracts",
AddressDecompiledContractController,
only: [:index],
as: :decompiled_contract
)
resources(
"/logs",
AddressLogsController,
only: [:index],
as: :logs
)
resources(
"/contract_verifications",
AddressContractVerificationController,
only: [:new],
as: :verify_contract
)
resources(
"/read_contract",
AddressReadContractController,
only: [:index, :show],
as: :read_contract
)
resources("/tokens", AddressTokenController, only: [:index], as: :token) do
resources(
"/token_transfers",
AddressTokenTransferController,
only: [:index],
as: :transfers
)
end
resources(
"/token_balances",
AddressTokenBalanceController,
only: [:index],
as: :token_balance
)
resources(
"/coin_balances",
AddressCoinBalanceController,
only: [:index],
as: :coin_balance
)
resources(
"/coin_balances/by_day",
AddressCoinBalanceByDayController,
only: [:index],
as: :coin_balance_by_day
)
end
resources "/tokens", Tokens.TokenController, only: [:show], as: :token do
resources(
"/token_transfers",
Tokens.TransferController,
only: [:index],
as: :transfer
)
resources(
"/read_contract",
Tokens.ReadContractController,
only: [:index],
as: :read_contract
)
resources(
"/token_holders",
Tokens.HolderController,
only: [:index],
as: :holder
)
resources(
"/inventory",
Tokens.InventoryController,
only: [:index],
as: :inventory
)
end
resources(
"/smart_contracts",
SmartContractController,
only: [:index, :show],
as: :smart_contract
)
get("/search", ChainController, :search)
get("/search_logs", AddressLogsController, :search_logs)
get("/transactions_csv", AddressTransactionController, :transactions_csv)
get("/token_autocomplete", ChainController, :token_autocomplete)
get("/token_transfers_csv", AddressTransactionController, :token_transfers_csv)
get("/chain_blocks", ChainController, :chain_blocks, as: :chain_blocks)
get("/api_docs", APIDocsController, :index)
get("/eth_rpc_api_docs", APIDocsController, :eth_rpc)
get("/*path", PageNotFoundController, :index)
end end
end end

@ -14,15 +14,14 @@
<!-- Pagination --> <!-- Pagination -->
<ul class="pagination"> <ul class="pagination">
<!-- First --> <!-- First -->
<%= if assigns[:first_page_path] do %> <li class="page-item">
<li class="page-item"> <a
<a disabled
<%= if !assigns[:first_page_path] do %>disabled<% end %> class="page-link"
class="page-link" href
href='<%= "#{assigns[:first_page_path]}" %>' data-first-page-button
>First</a> >First</a>
</li> </li>
<% end %>
<!-- Previous --> <!-- Previous -->
<li class="page-item"> <li class="page-item">
<a <a

@ -1,4 +1,4 @@
<div data-selector="loading-message" class="tile tile-type-loading"> <div data-loading-message data-selector="loading-message" class="tile tile-type-loading">
<div class="row tile-body"> <div class="row tile-body">
<div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column"> <div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">
<span class="tile-label"> <span class="tile-label">
@ -22,7 +22,7 @@
</div> </div>
</div> </div>
</div> </div>
<div data-selector="loading-message" class="tile tile-type-loading"> <div data-loading-message data-selector="loading-message" class="tile tile-type-loading">
<div class="row tile-body"> <div class="row tile-body">
<div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column"> <div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">
<span class="tile-label"> <span class="tile-label">
@ -46,7 +46,7 @@
</div> </div>
</div> </div>
</div> </div>
<div data-selector="loading-message" class="tile tile-type-loading"> <div data-loading-message data-selector="loading-message" class="tile tile-type-loading">
<div class="row tile-body"> <div class="row tile-body">
<div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column"> <div class="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">
<span class="tile-label"> <span class="tile-label">

@ -3,7 +3,7 @@
<!-- Logo --> <!-- Logo -->
<div class="row footer-logo-row"> <div class="row footer-logo-row">
<div class="col-md-12"> <div class="col-md-12">
<%= link to: chain_path(@conn, :show), class: "footer-brand" do %> <%= link to: webapp_url(@conn), class: "footer-brand" do %>
<img class="footer-logo" src="<%= logo_footer() %>" alt="<%= subnetwork_title() %>" /> <img class="footer-logo" src="<%= logo_footer() %>" alt="<%= subnetwork_title() %>" />
<% end %> <% end %>
</div> </div>

@ -9,7 +9,7 @@
} }
</script> </script>
<div class="container-fluid navbar-container"> <div class="container-fluid navbar-container">
<%= link to: chain_path(@conn, :show), class: "navbar-brand", "data-test": "header_logo" do %> <%= link to: webapp_url(@conn), class: "navbar-brand", "data-test": "header_logo" do %>
<img class="navbar-logo" id="navbar-logo" src="<%= logo() %>" alt="<%= subnetwork_title() %>" /> <img class="navbar-logo" id="navbar-logo" src="<%= logo() %>" alt="<%= subnetwork_title() %>" />
<% end %> <% end %>
<script> <script>
@ -22,79 +22,83 @@
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item dropdown"> <%= if Application.get_env(:block_scout_web, BlockScoutWeb.WebRouter)[:enabled] do %>
<a class="nav-link topnav-nav-link dropdown-toggle" href="#" id="navbarBlocksDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <li class="nav-item dropdown">
<span class="nav-link-icon"> <a class="nav-link topnav-nav-link dropdown-toggle" href="#" id="navbarBlocksDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<%= render BlockScoutWeb.IconsView, "_block_icon.html" %> <span class="nav-link-icon">
</span> <%= render BlockScoutWeb.IconsView, "_block_icon.html" %>
<%= gettext("Blocks") %> </span>
</a>
<div class="dropdown-menu" aria-labelledby="navbarBlocksDropdown">
<%= link to: block_path(@conn, :index), class: "dropdown-item #{tab_status("blocks", @conn.request_path)}" do %>
<%= gettext("Blocks") %> <%= gettext("Blocks") %>
</a>
<div class="dropdown-menu" aria-labelledby="navbarBlocksDropdown">
<%= link to: block_path(@conn, :index), class: "dropdown-item #{tab_status("blocks", @conn.request_path)}" do %>
<%= gettext("Blocks") %>
<% end %>
<%= link to: uncle_path(@conn, :uncle), class: "dropdown-item #{tab_status("uncles", @conn.request_path)}" do %>
<%= gettext("Uncles") %>
<% end %>
<%= link to: reorg_path(@conn, :reorg), class: "dropdown-item #{tab_status("reorgs", @conn.request_path)}" do %>
<%= gettext("Forked Blocks (Reorgs)") %>
<% end %>
</div>
</li>
<li class="nav-item dropdown">
<a href="#" role="button" id="navbarTransactionsDropdown" class="nav-link topnav-nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_transaction_icon.html" %>
</span>
<%= gettext("Transactions") %>
</a>
<div class="dropdown-menu" aria-labeledby="navbarTransactionsDropdown">
<%= link(
gettext("Validated"),
class: "dropdown-item #{tab_status("txs", @conn.request_path)}",
to: transaction_path(@conn, :index)
) %>
<%= link(
gettext("Pending"),
class: "dropdown-item #{tab_status("pending_transactions", @conn.request_path)}",
"data-test": "pending_transactions_link",
to: pending_transaction_path(@conn, :index)
) %>
</div>
</li>
<li class="nav-item">
<%= link to: address_path(@conn, :index), class: "nav-link topnav-nav-link #{tab_status("accounts", @conn.request_path)}" do %>
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_accounts_icon.html" %>
</span>
<%= gettext("Accounts") %>
<% end %> <% end %>
<%= link to: uncle_path(@conn, :uncle), class: "dropdown-item #{tab_status("uncles", @conn.request_path)}" do %> </li>
<%= gettext("Uncles") %> <% end %>
<% end %> <%= if Application.get_env(:block_scout_web, BlockScoutWeb.ApiRouter)[:reading_enabled] || Application.get_env(:block_scout_web, :api_url) do %>
<%= link to: reorg_path(@conn, :reorg), class: "dropdown-item #{tab_status("reorgs", @conn.request_path)}" do %> <li class="nav-item dropdown">
<%= gettext("Forked Blocks (Reorgs)") %> <a href="#" role="button" id="navbarAPIsDropdown" class="nav-link topnav-nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<% end %> <span class="nav-link-icon">
</div> <%= render BlockScoutWeb.IconsView, "_api_icon.html" %>
</li> </span>
<li class="nav-item dropdown"> <%= gettext("APIs") %>
<a href="#" role="button" id="navbarTransactionsDropdown" class="nav-link topnav-nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> </a>
<span class="nav-link-icon"> <div class="dropdown-menu" aria-labeledby="navbarTransactionsDropdown">
<%= render BlockScoutWeb.IconsView, "_transaction_icon.html" %> <%= link(
</span> gettext("GraphQL"),
<%= gettext("Transactions") %> class: "dropdown-item #{tab_status("graphiql", @conn.request_path)}",
</a> to: api_url() <> "/graphiql"
<div class="dropdown-menu" aria-labeledby="navbarTransactionsDropdown"> ) %>
<%= link( <%= link(
gettext("Validated"), gettext("RPC"),
class: "dropdown-item #{tab_status("txs", @conn.request_path)}", class: "dropdown-item #{tab_status("api_docs", @conn.request_path)}",
to: transaction_path(@conn, :index) to: api_url() <> api_docs_path(@conn, :index)
) %> ) %>
<%= link( <%= link(
gettext("Pending"), gettext("Eth RPC"),
class: "dropdown-item #{tab_status("pending_transactions", @conn.request_path)}", class: "dropdown-item #{tab_status("eth_rpc_api_docs", @conn.request_path)}",
"data-test": "pending_transactions_link", to: api_url() <> api_docs_path(@conn, :eth_rpc)
to: pending_transaction_path(@conn, :index) ) %>
) %> </div>
</div> </li>
</li> <% end %>
<li class="nav-item">
<%= link to: address_path(@conn, :index), class: "nav-link topnav-nav-link #{tab_status("accounts", @conn.request_path)}" do %>
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_accounts_icon.html" %>
</span>
<%= gettext("Accounts") %>
<% end %>
</li>
<li class="nav-item dropdown">
<a href="#" role="button" id="navbarAPIsDropdown" class="nav-link topnav-nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_api_icon.html" %>
</span>
<%= gettext("APIs") %>
</a>
<div class="dropdown-menu" aria-labeledby="navbarTransactionsDropdown">
<%= link(
gettext("GraphQL"),
class: "dropdown-item #{tab_status("graphiql", @conn.request_path)}",
to: "/graphiql"
) %>
<%= link(
gettext("RPC"),
class: "dropdown-item #{tab_status("api_docs", @conn.request_path)}",
to: api_docs_path(@conn, :index)
) %>
<%= link(
gettext("Eth RPC"),
class: "dropdown-item #{tab_status("api_docs", @conn.request_path)}",
to: api_docs_path(@conn, :eth_rpc)
) %>
</div>
</li>
<li class="nav-item dropdown nav-item-networks"> <li class="nav-item dropdown nav-item-networks">
<a class="nav-link topnav-nav-link active-icon <%= if dropdown_nets() != [], do: "dropdown-toggle js-show-network-selector" %>" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link topnav-nav-link active-icon <%= if dropdown_nets() != [], do: "dropdown-toggle js-show-network-selector" %>" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon"> <span class="nav-link-icon">
@ -111,40 +115,42 @@
</svg> </svg>
</button> </button>
<!-- Search navbar --> <!-- Search navbar -->
<div class="search-form d-lg-flex d-inline-block" style="background-color: #22223a"> <%= if Application.get_env(:block_scout_web, BlockScoutWeb.WebRouter)[:enabled] do %>
<%= form_for @conn, chain_path(@conn, :search), [class: "form-inline my-2 my-lg-0", method: :get, enforce_utf8: false], fn f -> %> <div class="search-form d-lg-flex d-inline-block" style="background-color: #22223a">
<div class="input-group" title='<%= gettext("Search by address, token symbol name, transaction hash, or block number") %>'> <%= form_for @conn, chain_path(@conn, :search), [class: "form-inline my-2 my-lg-0", method: :get, enforce_utf8: false], fn f -> %>
<%= awesomplete(f, :q, <div class="input-group" title='<%= gettext("Search by address, token symbol name, transaction hash, or block number") %>'>
[ <%= awesomplete(f, :q,
class: "form-control me auto", [
placeholder: gettext("Search by address, token symbol name, transaction hash, or block number"), class: "form-control me auto",
"aria-describedby": "search-icon", placeholder: gettext("Search by address, token symbol name, transaction hash, or block number"),
"aria-label": gettext("Search"), "aria-describedby": "search-icon",
"data-test": "search_input" "aria-label": gettext("Search"),
], "data-test": "search_input"
[ url: "#{chain_path(@conn, :token_autocomplete)}?q=", ],
limit: 0, [ url: "#{chain_path(@conn, :token_autocomplete)}?q=",
minChars: 2, limit: 0,
value: "contract_address_hash", minChars: 2,
label: "contract_address_hash", value: "contract_address_hash",
descrSearch: true, label: "contract_address_hash",
descr: "symbol" descrSearch: true,
]) %> descr: "symbol"
<div class="input-group-append"> ]) %>
<button class="input-group-text" id="search-icon"> <div class="input-group-append">
<%= render BlockScoutWeb.IconsView, "_search_icon.html" %> <button class="input-group-text" id="search-icon">
</button> <%= render BlockScoutWeb.IconsView, "_search_icon.html" %>
</div> </button>
</div> </div>
<button class="btn btn-outline-success my-2 my-sm-0 sr-only hidden" type="submit"><%= gettext "Search" %></button> </div>
<script> <button class="btn btn-outline-success my-2 my-sm-0 sr-only hidden" type="submit"><%= gettext "Search" %></button>
if (localStorage.getItem("current-color-mode") === "dark") { <script>
document.getElementById("q").style.backgroundColor = "#22223a"; if (localStorage.getItem("current-color-mode") === "dark") {
document.getElementById("q").style.borderColor = "#22223a"; document.getElementById("q").style.backgroundColor = "#22223a";
} document.getElementById("q").style.borderColor = "#22223a";
</script> }
<% end %> </script>
</div> <% end %>
</div>
<% end %>
</div> </div>
</div> </div>
</nav> </nav>

@ -1,7 +1,7 @@
<div data-test="transaction_log" class="tile tile-muted"> <div data-test="transaction_log" class="tile tile-muted">
<dl class="row"> <dl class="row">
<dt class="col-md-1"> <%= gettext "Address" %> </dt> <dt class="col-lg-2"> <%= gettext "Address" %> </dt>
<dd class="col-md-11"> <dd class="col-lg-10">
<h3 class=""> <h3 class="">
<%= link( <%= link(
@log.address, @log.address,
@ -13,8 +13,8 @@
</dd> </dd>
<%= case decode(@log, @transaction) do %> <%= case decode(@log, @transaction) do %>
<% {:error, :contract_not_verified} -> %> <% {:error, :contract_not_verified} -> %>
<dt class="col-md-1"><%= gettext "Decoded" %></dt> <dt class="col-lg-2"><%= gettext "Decoded" %></dt>
<dd class="col-md-11"> <dd class="col-lg-10">
<div class="alert alert-info"> <div class="alert alert-info">
<%= gettext "To see decoded input data, the contract must be verified." %> <%= gettext "To see decoded input data, the contract must be verified." %>
<%= case @transaction do %> <%= case @transaction do %>
@ -25,16 +25,16 @@
<% end %> <% end %>
</div> </div>
<% {:error, :could_not_decode} -> %> <% {:error, :could_not_decode} -> %>
<dt class="col-md-1"><%= gettext "Decoded" %></dt> <dt class="col-lg-2"><%= gettext "Decoded" %></dt>
<dd class="col-md-11"> <dd class="col-lg-10">
<div class="alert alert-danger"> <div class="alert alert-danger">
<%= gettext "Failed to decode log data." %> <%= gettext "Failed to decode log data." %>
</div> </div>
<% {:error, :no_matching_function} -> %> <% {:error, :no_matching_function} -> %>
<%= nil %> <%= nil %>
<% {:ok, method_id, text, mapping} -> %> <% {:ok, method_id, text, mapping} -> %>
<dt class="col-md-1"><%= gettext "Decoded" %></dt> <dt class="col-lg-2"><%= gettext "Decoded" %></dt>
<dd class="col-md-11"> <dd class="col-lg-10">
<table summary="Transaction Info" class="table thead-light table-bordered transaction-input-table"> <table summary="Transaction Info" class="table thead-light table-bordered transaction-input-table">
<tr> <tr>
<td>Method Id</td> <td>Method Id</td>
@ -88,8 +88,8 @@
<%= nil %> <%= nil %>
<% end %> <% end %>
<dt class="col-md-1"><%= gettext "Topics" %></dt> <dt class="col-lg-2"><%= gettext "Topics" %></dt>
<dd class="col-md-11"> <dd class="col-lg-10">
<div class="raw-transaction-log-topics"> <div class="raw-transaction-log-topics">
<%= unless is_nil(@log.first_topic) do %> <%= unless is_nil(@log.first_topic) do %>
<div class="text-dark"> <div class="text-dark">
@ -117,10 +117,10 @@
<% end %> <% end %>
</div> </div>
</dd> </dd>
<dt class="col-md-1"> <dt class="col-lg-2">
<%= gettext "Data" %> <%= gettext "Data" %>
</dt> </dt>
<dd class="col-md-11"> <dd class="col-lg-10">
<%= unless is_nil(@log.data) do %> <%= unless is_nil(@log.data) do %>
<div class="text-dark raw-transaction-log-data"> <div class="text-dark raw-transaction-log-data">
<%= @log.data %> <%= @log.data %>

@ -68,6 +68,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionView do
"input" => "#{transaction.input}", "input" => "#{transaction.input}",
"gasLimit" => "#{transaction.gas}", "gasLimit" => "#{transaction.gas}",
"gasUsed" => "#{transaction.gas_used}", "gasUsed" => "#{transaction.gas_used}",
"gasPrice" => "#{transaction.gas_price.value}",
"logs" => Enum.map(logs, &prepare_log/1), "logs" => Enum.map(logs, &prepare_log/1),
"next_page_params" => next_page_params "next_page_params" => next_page_params
} }

@ -228,4 +228,33 @@ defmodule BlockScoutWeb.LayoutView do
[] []
end end
end end
def webapp_url(conn) do
:block_scout_web
|> Application.get_env(:webapp_url)
|> validate_url()
|> case do
:error -> chain_path(conn, :show)
{:ok, url} -> url
end
end
def api_url do
:block_scout_web
|> Application.get_env(:api_url)
|> validate_url()
|> case do
:error -> ""
{:ok, url} -> url
end
end
defp validate_url(url) when is_binary(url) do
case URI.parse(url) do
%URI{host: nil} -> :error
_ -> {:ok, url}
end
end
defp validate_url(_), do: :error
end end

@ -40,22 +40,43 @@ defmodule BlockScoutWeb.TransactionView do
end end
def aggregate_token_transfers(token_transfers) do def aggregate_token_transfers(token_transfers) do
token_transfers {transfers, nft_transfers} =
|> Enum.reduce(%{}, fn token_transfer, acc -> token_transfers
new_entry = %{ |> Enum.reduce({%{}, []}, fn token_transfer, acc ->
token: token_transfer.token, aggregate_reducer(token_transfer, acc)
amount: token_transfer.amount, end)
token_id: token_transfer.token_id
}
existing_entry = Map.get(acc, token_transfer.token_contract_address, %{new_entry | amount: Decimal.new(0)}) final_transfers = Map.values(transfers)
Map.put(acc, token_transfer.token_contract_address, %{ final_transfers ++ nft_transfers
end
defp aggregate_reducer(%{amount: amount} = token_transfer, {acc1, acc2}) when is_nil(amount) do
new_entry = %{
token: token_transfer.token,
amount: nil,
token_id: token_transfer.token_id
}
{acc1, [new_entry | acc2]}
end
defp aggregate_reducer(token_transfer, {acc1, acc2}) do
new_entry = %{
token: token_transfer.token,
amount: token_transfer.amount,
token_id: token_transfer.token_id
}
existing_entry = Map.get(acc1, token_transfer.token_contract_address, %{new_entry | amount: Decimal.new(0)})
new_acc1 =
Map.put(acc1, token_transfer.token_contract_address, %{
new_entry new_entry
| amount: Decimal.add(new_entry.amount, existing_entry.amount) | amount: Decimal.add(new_entry.amount, existing_entry.amount)
}) })
end)
|> Enum.map(fn {_key, value} -> value end) {new_acc1, acc2}
end end
def token_type_name(type) do def token_type_name(type) do

@ -0,0 +1,204 @@
defmodule BlockScoutWeb.WebRouter do
@moduledoc """
Router for web app
"""
use BlockScoutWeb, :router
pipeline :browser do
plug(:accepts, ["html"])
plug(:fetch_session)
plug(:fetch_flash)
plug(:protect_from_forgery)
plug(BlockScoutWeb.CSPHeader)
end
# Disallows Iframes (write routes)
scope "/", BlockScoutWeb do
pipe_through(:browser)
end
# Allows Iframes (read-only routes)
scope "/", BlockScoutWeb do
pipe_through([:browser, BlockScoutWeb.Plug.AllowIframe])
resources("/", ChainController, only: [:show], singleton: true, as: :chain)
resources("/market_history_chart", Chain.MarketHistoryChartController,
only: [:show],
singleton: true
)
resources "/blocks", BlockController, only: [:index, :show], param: "hash_or_number" do
resources("/transactions", BlockTransactionController, only: [:index], as: :transaction)
end
get("/reorgs", BlockController, :reorg, as: :reorg)
get("/uncles", BlockController, :uncle, as: :uncle)
resources("/pending_transactions", PendingTransactionController, only: [:index])
resources("/recent_transactions", RecentTransactionsController, only: [:index])
get("/txs", TransactionController, :index)
resources "/tx", TransactionController, only: [:show] do
resources(
"/internal_transactions",
TransactionInternalTransactionController,
only: [:index],
as: :internal_transaction
)
resources(
"/raw_trace",
TransactionRawTraceController,
only: [:index],
as: :raw_trace
)
resources("/logs", TransactionLogController, only: [:index], as: :log)
resources("/token_transfers", TransactionTokenTransferController,
only: [:index],
as: :token_transfer
)
end
resources("/accounts", AddressController, only: [:index])
resources "/address", AddressController, only: [:show] do
resources("/transactions", AddressTransactionController, only: [:index], as: :transaction)
resources(
"/internal_transactions",
AddressInternalTransactionController,
only: [:index],
as: :internal_transaction
)
resources(
"/validations",
AddressValidationController,
only: [:index],
as: :validation
)
resources(
"/contracts",
AddressContractController,
only: [:index],
as: :contract
)
resources(
"/decompiled_contracts",
AddressDecompiledContractController,
only: [:index],
as: :decompiled_contract
)
resources(
"/logs",
AddressLogsController,
only: [:index],
as: :logs
)
resources(
"/contract_verifications",
AddressContractVerificationController,
only: [:new],
as: :verify_contract
)
resources(
"/read_contract",
AddressReadContractController,
only: [:index, :show],
as: :read_contract
)
resources("/tokens", AddressTokenController, only: [:index], as: :token) do
resources(
"/token_transfers",
AddressTokenTransferController,
only: [:index],
as: :transfers
)
end
resources(
"/token_balances",
AddressTokenBalanceController,
only: [:index],
as: :token_balance
)
resources(
"/coin_balances",
AddressCoinBalanceController,
only: [:index],
as: :coin_balance
)
resources(
"/coin_balances/by_day",
AddressCoinBalanceByDayController,
only: [:index],
as: :coin_balance_by_day
)
end
resources "/tokens", Tokens.TokenController, only: [:show], as: :token do
resources(
"/token_transfers",
Tokens.TransferController,
only: [:index],
as: :transfer
)
resources(
"/read_contract",
Tokens.ReadContractController,
only: [:index],
as: :read_contract
)
resources(
"/token_holders",
Tokens.HolderController,
only: [:index],
as: :holder
)
resources(
"/inventory",
Tokens.InventoryController,
only: [:index],
as: :inventory
)
end
resources(
"/smart_contracts",
SmartContractController,
only: [:index, :show],
as: :smart_contract
)
get("/search", ChainController, :search)
get("/search_logs", AddressLogsController, :search_logs)
get("/transactions_csv", AddressTransactionController, :transactions_csv)
get("/token_autocomplete", ChainController, :token_autocomplete)
get("/token_transfers_csv", AddressTransactionController, :token_transfers_csv)
get("/chain_blocks", ChainController, :chain_blocks, as: :chain_blocks)
get("/*path", PageNotFoundController, :index)
end
end

@ -64,7 +64,7 @@ defmodule BlockScoutWeb.Mixfile do
# Integrates Absinthe subscriptions with Phoenix # Integrates Absinthe subscriptions with Phoenix
{:absinthe_phoenix, git: "https://github.com/ayrat555/absinthe_phoenix.git", branch: "master"}, {:absinthe_phoenix, git: "https://github.com/ayrat555/absinthe_phoenix.git", branch: "master"},
# Plug support for Absinthe # Plug support for Absinthe
{:absinthe_plug, git: "https://github.com/ayrat555/absinthe_plug.git", branch: "ab-allow-to-set-default-query"}, {:absinthe_plug, git: "https://github.com/ayrat555/absinthe_plug.git", branch: "ab-enable-default-query"},
# Absinthe support for the Relay framework # Absinthe support for the Relay framework
{:absinthe_relay, "~> 1.4"}, {:absinthe_relay, "~> 1.4"},
{:bypass, "~> 1.0", only: :test}, {:bypass, "~> 1.0", only: :test},
@ -102,7 +102,7 @@ defmodule BlockScoutWeb.Mixfile do
{:phoenix, "~> 1.4"}, {:phoenix, "~> 1.4"},
{:phoenix_ecto, "~> 4.0"}, {:phoenix_ecto, "~> 4.0"},
{:phoenix_html, "~> 2.10"}, {:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: [:dev]}, {:phoenix_live_reload, "~> 1.2", only: [:dev]},
{:phoenix_pubsub, "~> 1.0"}, {:phoenix_pubsub, "~> 1.0"},
# use `:cowboy` for WebServer with `:plug` # use `:cowboy` for WebServer with `:plug`
{:plug_cowboy, "~> 2.0"}, {:plug_cowboy, "~> 2.0"},

@ -49,7 +49,7 @@ msgid "%{subnetwork} Explorer - BlockScout"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:163 #: lib/block_scout_web/views/transaction_view.ex:184
msgid "(Awaiting internal transactions for status)" msgid "(Awaiting internal transactions for status)"
msgstr "" msgstr ""
@ -87,7 +87,7 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70 #: lib/block_scout_web/templates/layout/_topnav.html.eex:71
msgid "Accounts" msgid "Accounts"
msgstr "" msgstr ""
@ -178,8 +178,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87 #: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/layout/_topnav.html.eex:30 #: lib/block_scout_web/templates/layout/_topnav.html.eex:31
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34 #: lib/block_scout_web/templates/layout/_topnav.html.eex:35
msgid "Blocks" msgid "Blocks"
msgstr "" msgstr ""
@ -276,12 +276,12 @@ msgid "Contract Address Pending"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:240 #: lib/block_scout_web/views/transaction_view.ex:261
msgid "Contract Call" msgid "Contract Call"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:239 #: lib/block_scout_web/views/transaction_view.ex:260
msgid "Contract Creation" msgid "Contract Creation"
msgstr "" msgstr ""
@ -356,12 +356,12 @@ msgid "Error trying to fetch balances."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:167 #: lib/block_scout_web/views/transaction_view.ex:188
msgid "Error: %{reason}" msgid "Error: %{reason}"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:165 #: lib/block_scout_web/views/transaction_view.ex:186
msgid "Error: (Awaiting internal transactions for reason)" msgid "Error: (Awaiting internal transactions for reason)"
msgstr "" msgstr ""
@ -392,7 +392,7 @@ msgid "Fetching tokens..."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40 #: lib/block_scout_web/templates/layout/_topnav.html.eex:41
msgid "Forked Blocks (Reorgs)" msgid "Forked Blocks (Reorgs)"
msgstr "" msgstr ""
@ -466,7 +466,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6
#: lib/block_scout_web/views/address_view.ex:306 #: lib/block_scout_web/views/address_view.ex:306
#: lib/block_scout_web/views/transaction_view.ex:293 #: lib/block_scout_web/views/transaction_view.ex:314
msgid "Internal Transactions" msgid "Internal Transactions"
msgstr "" msgstr ""
@ -493,7 +493,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8
#: lib/block_scout_web/views/address_view.ex:312 #: lib/block_scout_web/views/address_view.ex:312
#: lib/block_scout_web/views/transaction_view.ex:294 #: lib/block_scout_web/views/transaction_view.ex:315
msgid "Logs" msgid "Logs"
msgstr "" msgstr ""
@ -506,8 +506,8 @@ msgid "Market Cap"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:148 #: lib/block_scout_web/views/transaction_view.ex:169
#: lib/block_scout_web/views/transaction_view.ex:148 #: lib/block_scout_web/views/transaction_view.ex:169
msgid "Max of" msgid "Max of"
msgstr "" msgstr ""
@ -597,9 +597,9 @@ msgid "Parent Hash"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:58 #: lib/block_scout_web/templates/layout/_topnav.html.eex:59
#: lib/block_scout_web/views/transaction_view.ex:162 #: lib/block_scout_web/views/transaction_view.ex:183
#: lib/block_scout_web/views/transaction_view.ex:196 #: lib/block_scout_web/views/transaction_view.ex:217
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
@ -662,8 +662,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14 #: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:122 #: lib/block_scout_web/templates/layout/_topnav.html.eex:127
#: lib/block_scout_web/templates/layout/_topnav.html.eex:139 #: lib/block_scout_web/templates/layout/_topnav.html.eex:144
msgid "Search" msgid "Search"
msgstr "" msgstr ""
@ -686,7 +686,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8
#: lib/block_scout_web/views/transaction_view.ex:164 #: lib/block_scout_web/views/transaction_view.ex:185
msgid "Success" msgid "Success"
msgstr "" msgstr ""
@ -791,7 +791,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:5 #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:5
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:238 #: lib/block_scout_web/views/transaction_view.ex:259
msgid "Token Transfer" msgid "Token Transfer"
msgstr "" msgstr ""
@ -801,7 +801,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: 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/templates/transaction_token_transfer/index.html.eex:7
#: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:292 #: lib/block_scout_web/views/transaction_view.ex:313
msgid "Token Transfers" msgid "Token Transfers"
msgstr "" msgstr ""
@ -835,7 +835,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:3 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:3
#: lib/block_scout_web/views/transaction_view.ex:241 #: lib/block_scout_web/views/transaction_view.ex:262
msgid "Transaction" msgid "Transaction"
msgstr "" msgstr ""
@ -860,7 +860,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10 #: 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/block_transaction/index.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:108 #: lib/block_scout_web/templates/chain/show.html.eex:108
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49 #: lib/block_scout_web/templates/layout/_topnav.html.eex:50
#: lib/block_scout_web/views/address_view.ex:305 #: lib/block_scout_web/views/address_view.ex:305
msgid "Transactions" msgid "Transactions"
msgstr "" msgstr ""
@ -887,7 +887,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:80 #: lib/block_scout_web/templates/block/overview.html.eex:80
#: lib/block_scout_web/templates/layout/_topnav.html.eex:37 #: lib/block_scout_web/templates/layout/_topnav.html.eex:38
msgid "Uncles" msgid "Uncles"
msgstr "" msgstr ""
@ -902,7 +902,7 @@ msgid "Used"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:53 #: lib/block_scout_web/templates/layout/_topnav.html.eex:54
msgid "Validated" msgid "Validated"
msgstr "" msgstr ""
@ -1048,17 +1048,17 @@ msgid "Loading...."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:78 #: lib/block_scout_web/templates/layout/_topnav.html.eex:81
msgid "APIs" msgid "APIs"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:82 #: lib/block_scout_web/templates/layout/_topnav.html.eex:85
msgid "GraphQL" msgid "GraphQL"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:87 #: lib/block_scout_web/templates/layout/_topnav.html.eex:90
msgid "RPC" msgid "RPC"
msgstr "" msgstr ""
@ -1434,8 +1434,8 @@ msgid "Error: Could not determine contract creator."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:116 #: lib/block_scout_web/templates/layout/_topnav.html.eex:121
#: lib/block_scout_web/templates/layout/_topnav.html.eex:120 #: lib/block_scout_web/templates/layout/_topnav.html.eex:125
msgid "Search by address, token symbol name, transaction hash, or block number" msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr "" msgstr ""
@ -1499,7 +1499,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:24
#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 #: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7
#: lib/block_scout_web/views/transaction_view.ex:295 #: lib/block_scout_web/views/transaction_view.ex:316
msgid "Raw Trace" msgid "Raw Trace"
msgstr "" msgstr ""
@ -1510,7 +1510,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: #:
#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:40 #: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:39
msgid "Page" msgid "Page"
msgstr "" msgstr ""
@ -1528,7 +1528,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: #:
#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:40 #: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:39
msgid "of" msgid "of"
msgstr "" msgstr ""
@ -1661,7 +1661,7 @@ msgid "ETH RPC API Documentation"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:92 #: lib/block_scout_web/templates/layout/_topnav.html.eex:95
msgid "Eth RPC" msgid "Eth RPC"
msgstr "" msgstr ""
@ -1707,12 +1707,12 @@ msgid "Change Network"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:63 #: lib/block_scout_web/views/transaction_view.ex:84
msgid "ERC-20 " msgid "ERC-20 "
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:64 #: lib/block_scout_web/views/transaction_view.ex:85
msgid "ERC-721 " msgid "ERC-721 "
msgstr "" msgstr ""

@ -49,7 +49,7 @@ msgid "%{subnetwork} Explorer - BlockScout"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:163 #: lib/block_scout_web/views/transaction_view.ex:184
msgid "(Awaiting internal transactions for status)" msgid "(Awaiting internal transactions for status)"
msgstr "" msgstr ""
@ -87,7 +87,7 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70 #: lib/block_scout_web/templates/layout/_topnav.html.eex:71
msgid "Accounts" msgid "Accounts"
msgstr "" msgstr ""
@ -178,8 +178,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87 #: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/layout/_topnav.html.eex:30 #: lib/block_scout_web/templates/layout/_topnav.html.eex:31
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34 #: lib/block_scout_web/templates/layout/_topnav.html.eex:35
msgid "Blocks" msgid "Blocks"
msgstr "" msgstr ""
@ -276,12 +276,12 @@ msgid "Contract Address Pending"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:240 #: lib/block_scout_web/views/transaction_view.ex:261
msgid "Contract Call" msgid "Contract Call"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:239 #: lib/block_scout_web/views/transaction_view.ex:260
msgid "Contract Creation" msgid "Contract Creation"
msgstr "" msgstr ""
@ -356,12 +356,12 @@ msgid "Error trying to fetch balances."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:167 #: lib/block_scout_web/views/transaction_view.ex:188
msgid "Error: %{reason}" msgid "Error: %{reason}"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:165 #: lib/block_scout_web/views/transaction_view.ex:186
msgid "Error: (Awaiting internal transactions for reason)" msgid "Error: (Awaiting internal transactions for reason)"
msgstr "" msgstr ""
@ -392,7 +392,7 @@ msgid "Fetching tokens..."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40 #: lib/block_scout_web/templates/layout/_topnav.html.eex:41
msgid "Forked Blocks (Reorgs)" msgid "Forked Blocks (Reorgs)"
msgstr "" msgstr ""
@ -466,7 +466,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6
#: lib/block_scout_web/views/address_view.ex:306 #: lib/block_scout_web/views/address_view.ex:306
#: lib/block_scout_web/views/transaction_view.ex:293 #: lib/block_scout_web/views/transaction_view.ex:314
msgid "Internal Transactions" msgid "Internal Transactions"
msgstr "" msgstr ""
@ -493,7 +493,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8
#: lib/block_scout_web/views/address_view.ex:312 #: lib/block_scout_web/views/address_view.ex:312
#: lib/block_scout_web/views/transaction_view.ex:294 #: lib/block_scout_web/views/transaction_view.ex:315
msgid "Logs" msgid "Logs"
msgstr "" msgstr ""
@ -506,8 +506,8 @@ msgid "Market Cap"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:148 #: lib/block_scout_web/views/transaction_view.ex:169
#: lib/block_scout_web/views/transaction_view.ex:148 #: lib/block_scout_web/views/transaction_view.ex:169
msgid "Max of" msgid "Max of"
msgstr "" msgstr ""
@ -597,9 +597,9 @@ msgid "Parent Hash"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:58 #: lib/block_scout_web/templates/layout/_topnav.html.eex:59
#: lib/block_scout_web/views/transaction_view.ex:162 #: lib/block_scout_web/views/transaction_view.ex:183
#: lib/block_scout_web/views/transaction_view.ex:196 #: lib/block_scout_web/views/transaction_view.ex:217
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
@ -662,8 +662,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14 #: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:122 #: lib/block_scout_web/templates/layout/_topnav.html.eex:127
#: lib/block_scout_web/templates/layout/_topnav.html.eex:139 #: lib/block_scout_web/templates/layout/_topnav.html.eex:144
msgid "Search" msgid "Search"
msgstr "" msgstr ""
@ -686,7 +686,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8
#: lib/block_scout_web/views/transaction_view.ex:164 #: lib/block_scout_web/views/transaction_view.ex:185
msgid "Success" msgid "Success"
msgstr "" msgstr ""
@ -791,7 +791,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:5 #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:5
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:238 #: lib/block_scout_web/views/transaction_view.ex:259
msgid "Token Transfer" msgid "Token Transfer"
msgstr "" msgstr ""
@ -801,7 +801,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: 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/templates/transaction_token_transfer/index.html.eex:7
#: lib/block_scout_web/views/tokens/overview_view.ex:35 #: lib/block_scout_web/views/tokens/overview_view.ex:35
#: lib/block_scout_web/views/transaction_view.ex:292 #: lib/block_scout_web/views/transaction_view.ex:313
msgid "Token Transfers" msgid "Token Transfers"
msgstr "" msgstr ""
@ -835,7 +835,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:3 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:3
#: lib/block_scout_web/views/transaction_view.ex:241 #: lib/block_scout_web/views/transaction_view.ex:262
msgid "Transaction" msgid "Transaction"
msgstr "" msgstr ""
@ -860,7 +860,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10 #: 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/block_transaction/index.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:108 #: lib/block_scout_web/templates/chain/show.html.eex:108
#: lib/block_scout_web/templates/layout/_topnav.html.eex:49 #: lib/block_scout_web/templates/layout/_topnav.html.eex:50
#: lib/block_scout_web/views/address_view.ex:305 #: lib/block_scout_web/views/address_view.ex:305
msgid "Transactions" msgid "Transactions"
msgstr "" msgstr ""
@ -887,7 +887,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:80 #: lib/block_scout_web/templates/block/overview.html.eex:80
#: lib/block_scout_web/templates/layout/_topnav.html.eex:37 #: lib/block_scout_web/templates/layout/_topnav.html.eex:38
msgid "Uncles" msgid "Uncles"
msgstr "" msgstr ""
@ -902,7 +902,7 @@ msgid "Used"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:53 #: lib/block_scout_web/templates/layout/_topnav.html.eex:54
msgid "Validated" msgid "Validated"
msgstr "" msgstr ""
@ -1048,17 +1048,17 @@ msgid "Loading...."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:78 #: lib/block_scout_web/templates/layout/_topnav.html.eex:81
msgid "APIs" msgid "APIs"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:82 #: lib/block_scout_web/templates/layout/_topnav.html.eex:85
msgid "GraphQL" msgid "GraphQL"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:87 #: lib/block_scout_web/templates/layout/_topnav.html.eex:90
msgid "RPC" msgid "RPC"
msgstr "" msgstr ""
@ -1435,8 +1435,8 @@ msgid "Error: Could not determine contract creator."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:116 #: lib/block_scout_web/templates/layout/_topnav.html.eex:121
#: lib/block_scout_web/templates/layout/_topnav.html.eex:120 #: lib/block_scout_web/templates/layout/_topnav.html.eex:125
msgid "Search by address, token symbol name, transaction hash, or block number" msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr "" msgstr ""
@ -1500,7 +1500,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:24
#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 #: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7
#: lib/block_scout_web/views/transaction_view.ex:295 #: lib/block_scout_web/views/transaction_view.ex:316
msgid "Raw Trace" msgid "Raw Trace"
msgstr "" msgstr ""
@ -1511,7 +1511,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: #:
#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:40 #: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:39
msgid "Page" msgid "Page"
msgstr "" msgstr ""
@ -1529,7 +1529,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: #:
#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:40 #: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:39
msgid "of" msgid "of"
msgstr "" msgstr ""
@ -1662,7 +1662,7 @@ msgid "ETH RPC API Documentation"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:92 #: lib/block_scout_web/templates/layout/_topnav.html.eex:95
msgid "Eth RPC" msgid "Eth RPC"
msgstr "" msgstr ""
@ -1708,12 +1708,12 @@ msgid "Change Network"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:63 #: lib/block_scout_web/views/transaction_view.ex:84
msgid "ERC-20 " msgid "ERC-20 "
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:64 #: lib/block_scout_web/views/transaction_view.ex:85
msgid "ERC-721 " msgid "ERC-721 "
msgstr "" msgstr ""

@ -14,7 +14,10 @@ defmodule BlockScoutWeb.AddressCoinBalanceByDayControllerTest do
response = json_response(conn, 200) response = json_response(conn, 200)
assert length(response) == 2 assert [
%{"date" => _, "value" => 2.0e-15},
%{"date" => _, "value" => 1.0e-15}
] = response
end end
end end
end end

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressContractControllerTest do defmodule BlockScoutWeb.AddressContractControllerTest do
use BlockScoutWeb.ConnCase, async: true use BlockScoutWeb.ConnCase, async: true
import BlockScoutWeb.Router.Helpers, only: [address_contract_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [address_contract_path: 3]
alias Explorer.Chain.Hash alias Explorer.Chain.Hash
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do
use BlockScoutWeb.ConnCase, async: true use BlockScoutWeb.ConnCase, async: true
import BlockScoutWeb.Router.Helpers, import BlockScoutWeb.WebRouter.Helpers,
only: [address_internal_transaction_path: 3, address_internal_transaction_path: 4] only: [address_internal_transaction_path: 3, address_internal_transaction_path: 4]
alias Explorer.Chain.{Block, InternalTransaction, Transaction} alias Explorer.Chain.{Block, InternalTransaction, Transaction}

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressTokenControllerTest do defmodule BlockScoutWeb.AddressTokenControllerTest do
use BlockScoutWeb.ConnCase, async: true use BlockScoutWeb.ConnCase, async: true
import BlockScoutWeb.Router.Helpers, only: [address_token_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [address_token_path: 3]
alias Explorer.Chain.{Token} alias Explorer.Chain.{Token}

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressTokenTransferControllerTest do defmodule BlockScoutWeb.AddressTokenTransferControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, import BlockScoutWeb.WebRouter.Helpers,
only: [address_token_transfers_path: 4, address_token_transfers_path: 5] only: [address_token_transfers_path: 4, address_token_transfers_path: 5]
alias Explorer.Chain.{Address, Token} alias Explorer.Chain.{Address, Token}

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressTransactionControllerTest do defmodule BlockScoutWeb.AddressTransactionControllerTest do
use BlockScoutWeb.ConnCase, async: true use BlockScoutWeb.ConnCase, async: true
import BlockScoutWeb.Router.Helpers, only: [address_transaction_path: 3, address_transaction_path: 4] import BlockScoutWeb.WebRouter.Helpers, only: [address_transaction_path: 3, address_transaction_path: 4]
alias Explorer.Chain.Transaction alias Explorer.Chain.Transaction
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token

@ -37,7 +37,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do
test "with a bad action atom", %{conn: conn} do test "with a bad action atom", %{conn: conn} do
conn = %Conn{conn | params: %{"module" => "test", "action" => "some_atom_that_should_not_exist"}} conn = %Conn{conn | params: %{"module" => "test", "action" => "some_atom_that_should_not_exist"}}
result = RPCTranslator.call(conn, %{"test" => TestController}) result = RPCTranslator.call(conn, %{"test" => {TestController, []}})
assert result.halted assert result.halted
assert response = json_response(result, 400) assert response = json_response(result, 400)
assert response["message"] =~ "Unknown action" assert response["message"] =~ "Unknown action"
@ -49,7 +49,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do
test "with an invalid controller action", %{conn: conn} do test "with an invalid controller action", %{conn: conn} do
conn = %Conn{conn | params: %{"module" => "test", "action" => "index"}} conn = %Conn{conn | params: %{"module" => "test", "action" => "index"}}
result = RPCTranslator.call(conn, %{"test" => TestController}) result = RPCTranslator.call(conn, %{"test" => {TestController, []}})
assert result.halted assert result.halted
assert response = json_response(result, 400) assert response = json_response(result, 400)
assert response["message"] =~ "Unknown action" assert response["message"] =~ "Unknown action"
@ -59,7 +59,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do
end end
test "with missing params", %{conn: conn} do test "with missing params", %{conn: conn} do
result = RPCTranslator.call(conn, %{"test" => TestController}) result = RPCTranslator.call(conn, %{"test" => {TestController, []}})
assert result.halted assert result.halted
assert response = json_response(result, 400) assert response = json_response(result, 400)
assert response["message"] =~ "'module' and 'action' are required" assert response["message"] =~ "'module' and 'action' are required"
@ -71,7 +71,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do
test "with a valid request", %{conn: conn} do test "with a valid request", %{conn: conn} do
conn = %Conn{conn | params: %{"module" => "test", "action" => "test_action"}} conn = %Conn{conn | params: %{"module" => "test", "action" => "test_action"}}
result = RPCTranslator.call(conn, %{"test" => TestController}) result = RPCTranslator.call(conn, %{"test" => {TestController, []}})
assert json_response(result, 200) == %{} assert json_response(result, 200) == %{}
end end
end end

@ -456,6 +456,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
"input" => "#{transaction.input}", "input" => "#{transaction.input}",
"gasLimit" => "#{transaction.gas}", "gasLimit" => "#{transaction.gas}",
"gasUsed" => "#{transaction.gas_used}", "gasUsed" => "#{transaction.gas_used}",
"gasPrice" => "#{transaction.gas_price.value}",
"logs" => [ "logs" => [
%{ %{
"address" => "#{address.hash}", "address" => "#{address.hash}",

@ -114,4 +114,8 @@ defmodule BlockScoutWeb.API.V1.DecompiledControllerTest do
assert request.status == 403 assert request.status == 403
end end
end end
defp api_v1_decompiled_smart_contract_path(conn, action) do
"/api" <> ApiRoutes.api_v1_decompiled_smart_contract_path(conn, action)
end
end end

@ -87,4 +87,8 @@ defmodule BlockScoutWeb.API.V1.HealthControllerTest do
} }
} = Poison.decode!(request.resp_body) } = Poison.decode!(request.resp_body)
end end
defp api_v1_health_path(conn, action) do
"/api" <> ApiRoutes.api_v1_health_path(conn, action)
end
end end

@ -10,4 +10,8 @@ defmodule BlockScoutWeb.API.V1.SupplyControllerTest do
assert response["total_supply"] == Chain.total_supply() assert response["total_supply"] == Chain.total_supply()
assert response["circulating_supply"] == Chain.circulating_supply() assert response["circulating_supply"] == Chain.circulating_supply()
end end
def api_v1_supply_path(conn, action) do
"/api" <> ApiRoutes.api_v1_supply_path(conn, action)
end
end end

@ -69,4 +69,8 @@ defmodule BlockScoutWeb.API.V1.VerifiedControllerTest do
assert response.status == 201 assert response.status == 201
assert Jason.decode!(response.resp_body) == %{"status" => "success"} assert Jason.decode!(response.resp_body) == %{"status" => "success"}
end end
defp api_v1_verified_smart_contract_path(conn, action) do
"/api" <> ApiRoutes.api_v1_verified_smart_contract_path(conn, action)
end
end end

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.BlockTransactionControllerTest do defmodule BlockScoutWeb.BlockTransactionControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [block_transaction_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [block_transaction_path: 3]
describe "GET index/2" do describe "GET index/2" do
test "with invalid block number", %{conn: conn} do test "with invalid block number", %{conn: conn} do

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.ChainControllerTest do
# ETS table is shared in `Explorer.Counters.AddressesWithBalanceCounter` # ETS table is shared in `Explorer.Counters.AddressesWithBalanceCounter`
async: false async: false
import BlockScoutWeb.Router.Helpers, only: [chain_path: 2, block_path: 3, transaction_path: 3, address_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [chain_path: 2, block_path: 3, transaction_path: 3, address_path: 3]
alias Explorer.Chain.Block alias Explorer.Chain.Block
alias Explorer.Counters.AddressesWithBalanceCounter alias Explorer.Counters.AddressesWithBalanceCounter

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.PendingTransactionControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
alias Explorer.Chain.{Hash, Transaction} alias Explorer.Chain.{Hash, Transaction}
import BlockScoutWeb.Router.Helpers, only: [pending_transaction_path: 2, pending_transaction_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [pending_transaction_path: 2, pending_transaction_path: 3]
describe "GET index/2" do describe "GET index/2" do
test "returns no transactions that are in a block", %{conn: conn} do test "returns no transactions that are in a block", %{conn: conn} do

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.RecentTransactionsControllerTest do defmodule BlockScoutWeb.RecentTransactionsControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [recent_transactions_path: 2] import BlockScoutWeb.WebRouter.Helpers, only: [recent_transactions_path: 2]
alias Explorer.Chain.Hash alias Explorer.Chain.Hash

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.TransactionControllerTest do defmodule BlockScoutWeb.TransactionControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, import BlockScoutWeb.WebRouter.Helpers,
only: [transaction_path: 3, transaction_internal_transaction_path: 3, transaction_token_transfer_path: 3] only: [transaction_path: 3, transaction_internal_transaction_path: 3, transaction_token_transfer_path: 3]
alias Explorer.Chain.Transaction alias Explorer.Chain.Transaction

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [transaction_internal_transaction_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [transaction_internal_transaction_path: 3]
alias Explorer.Chain.InternalTransaction alias Explorer.Chain.InternalTransaction
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.TransactionLogControllerTest do defmodule BlockScoutWeb.TransactionLogControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [transaction_log_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [transaction_log_path: 3]
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [transaction_token_transfer_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [transaction_token_transfer_path: 3]
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.TransactionLogsPage do
use Wallaby.DSL use Wallaby.DSL
import Wallaby.Query, only: [css: 1, css: 2] import Wallaby.Query, only: [css: 1, css: 2]
import BlockScoutWeb.Router.Helpers, only: [transaction_log_path: 3] import BlockScoutWeb.WebRouter.Helpers, only: [transaction_log_path: 3]
alias Explorer.Chain.Transaction alias Explorer.Chain.Transaction
alias BlockScoutWeb.Endpoint alias BlockScoutWeb.Endpoint

@ -268,5 +268,19 @@ defmodule BlockScoutWeb.TransactionViewTest do
assert Enum.count(result) == 1 assert Enum.count(result) == 1
assert List.first(result).amount == Decimal.new(3) assert List.first(result).amount == Decimal.new(3)
end end
test "does not aggregate NFT tokens" do
transaction =
:transaction
|> insert()
|> with_block()
token_transfer = insert(:token_transfer, transaction: transaction, amount: nil)
result = TransactionView.aggregate_token_transfers([token_transfer, token_transfer, token_transfer])
assert Enum.count(result) == 3
assert List.first(result).amount == nil
end
end end
end end

@ -20,6 +20,7 @@ defmodule BlockScoutWeb.ConnCase do
# Import conveniences for testing with connections # Import conveniences for testing with connections
use Phoenix.ConnTest use Phoenix.ConnTest
import BlockScoutWeb.Router.Helpers import BlockScoutWeb.Router.Helpers
import BlockScoutWeb.WebRouter.Helpers, except: [static_path: 2]
# The default endpoint for testing # The default endpoint for testing
@endpoint BlockScoutWeb.Endpoint @endpoint BlockScoutWeb.Endpoint
@ -27,6 +28,7 @@ defmodule BlockScoutWeb.ConnCase do
import Explorer.Factory import Explorer.Factory
alias BlockScoutWeb.AdminRouter.Helpers, as: AdminRoutes alias BlockScoutWeb.AdminRouter.Helpers, as: AdminRoutes
alias BlockScoutWeb.ApiRouter.Helpers, as: ApiRoutes
end end
end end

@ -51,7 +51,7 @@ config :explorer, Explorer.KnownTokens, enabled: true, store: :ets
config :explorer, Explorer.Integrations.EctoLogger, query_time_ms_threshold: :timer.seconds(2) config :explorer, Explorer.Integrations.EctoLogger, query_time_ms_threshold: :timer.seconds(2)
config :explorer, Explorer.Market.History.Cataloger, enabled: true config :explorer, Explorer.Market.History.Cataloger, enabled: System.get_env("DISABLE_INDEXER") != "true"
config :explorer, Explorer.Repo, migration_timestamps: [type: :utc_datetime_usec] config :explorer, Explorer.Repo, migration_timestamps: [type: :utc_datetime_usec]
@ -65,7 +65,7 @@ if System.get_env("METADATA_CONTRACT") && System.get_env("VALIDATORS_CONTRACT")
metadata_contract_address: System.get_env("METADATA_CONTRACT"), metadata_contract_address: System.get_env("METADATA_CONTRACT"),
validators_contract_address: System.get_env("VALIDATORS_CONTRACT") validators_contract_address: System.get_env("VALIDATORS_CONTRACT")
config :explorer, Explorer.Validator.MetadataProcessor, enabled: true config :explorer, Explorer.Validator.MetadataProcessor, enabled: System.get_env("DISABLE_INDEXER") != "true"
else else
config :explorer, Explorer.Validator.MetadataProcessor, enabled: false config :explorer, Explorer.Validator.MetadataProcessor, enabled: false
end end

@ -73,10 +73,12 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
# order so that row ShareLocks are grabbed in a consistent order # order so that row ShareLocks are grabbed in a consistent order
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.index})
final_changes_list = reject_pending_transactions(ordered_changes_list, repo)
{:ok, internal_transactions} = {:ok, internal_transactions} =
Import.insert_changes_list( Import.insert_changes_list(
repo, repo,
ordered_changes_list, final_changes_list,
conflict_target: [:transaction_hash, :index], conflict_target: [:transaction_hash, :index],
for: InternalTransaction, for: InternalTransaction,
on_conflict: on_conflict, on_conflict: on_conflict,
@ -156,6 +158,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
from( from(
t in Transaction, t in Transaction,
where: t.hash in ^ordered_transaction_hashes, where: t.hash in ^ordered_transaction_hashes,
where: not is_nil(t.block_hash),
update: [ update: [
set: [ set: [
internal_transactions_indexed_at: ^timestamps.updated_at, internal_transactions_indexed_at: ^timestamps.updated_at,
@ -180,10 +183,8 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
] ]
) )
transaction_count = Enum.count(ordered_transaction_hashes)
try do try do
{^transaction_count, result} = repo.update_all(query, [], timeout: timeout) {_transaction_count, result} = repo.update_all(query, [], timeout: timeout)
{:ok, result} {:ok, result}
rescue rescue
@ -191,4 +192,23 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
{:error, %{exception: postgrex_error, transaction_hashes: ordered_transaction_hashes}} {:error, %{exception: postgrex_error, transaction_hashes: ordered_transaction_hashes}}
end end
end end
defp reject_pending_transactions(ordered_changes_list, repo) do
transaction_hashes =
ordered_changes_list
|> Enum.map(& &1.transaction_hash)
|> Enum.dedup()
query =
from(t in Transaction,
where: t.hash in ^transaction_hashes,
where: is_nil(t.block_hash),
select: t.hash
)
pending_transactions = repo.all(query)
ordered_changes_list
|> Enum.reject(fn %{transaction_hash: hash} -> Enum.member?(pending_transactions, hash) end)
end
end end

@ -74,13 +74,13 @@ defmodule Explorer.Mixfile do
{:decimal, "~> 1.0"}, {:decimal, "~> 1.0"},
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
# `override: true` for `ex_machina` compatibility # `override: true` for `ex_machina` compatibility
{:ecto, "~> 3.0", override: true}, {:ecto, "~> 3.1", override: true},
# Storing blockchain data and derived data in PostgreSQL. # Storing blockchain data and derived data in PostgreSQL.
{:ecto_sql, "~> 3.1"}, {:ecto_sql, "~> 3.1"},
# JSONRPC access to query smart contracts # JSONRPC access to query smart contracts
{:ethereum_jsonrpc, in_umbrella: true}, {:ethereum_jsonrpc, in_umbrella: true},
# Data factory for testing # Data factory for testing
{:ex_machina, "~> 2.1", only: [:test]}, {:ex_machina, "~> 2.3", only: [:test]},
# Code coverage # Code coverage
{:excoveralls, "~> 0.10.0", only: [:test], github: "KronicDeth/excoveralls", branch: "circle-workflows"}, {:excoveralls, "~> 0.10.0", only: [:test], github: "KronicDeth/excoveralls", branch: "circle-workflows"},
{:exvcr, "~> 0.10", only: :test}, {:exvcr, "~> 0.10", only: :test},

@ -2,7 +2,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do
use Explorer.DataCase use Explorer.DataCase
alias Ecto.Multi alias Ecto.Multi
alias Explorer.Chain.{Data, Wei, Transaction} alias Explorer.Chain.{Data, Wei, Transaction, InternalTransaction}
alias Explorer.Chain.Import.Runner.InternalTransactions alias Explorer.Chain.Import.Runner.InternalTransactions
describe "run/1" do describe "run/1" do
@ -20,6 +20,28 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do
assert :error == Repo.get(Transaction, transaction.hash).status assert :error == Repo.get(Transaction, transaction.hash).status
end end
test "pending transactions don't get updated not its internal_transactions inserted" do
transaction = insert(:transaction) |> with_block(status: :ok)
pending = insert(:transaction)
assert :ok == transaction.status
assert is_nil(pending.block_hash)
index = 0
transaction_changes = make_internal_transaction_changes(transaction.hash, index, nil)
pending_changes = make_internal_transaction_changes(pending.hash, index, nil)
assert {:ok, _} = run_internal_transactions([transaction_changes, pending_changes])
assert %InternalTransaction{} =
Repo.one(from(i in InternalTransaction, where: i.transaction_hash == ^transaction.hash))
assert from(i in InternalTransaction, where: i.transaction_hash == ^pending.hash) |> Repo.one() |> is_nil()
assert is_nil(Repo.get(Transaction, pending.hash).block_hash)
end
end end
defp run_internal_transactions(changes_list) when is_list(changes_list) do defp run_internal_transactions(changes_list) when is_list(changes_list) do

@ -42,6 +42,8 @@ config :indexer,
# config :indexer, Indexer.Fetcher.BlockReward.Supervisor, disabled?: true # config :indexer, Indexer.Fetcher.BlockReward.Supervisor, disabled?: true
config :indexer, Indexer.Fetcher.StakingPools.Supervisor, disabled?: true config :indexer, Indexer.Fetcher.StakingPools.Supervisor, disabled?: true
config :indexer, Indexer.Supervisor, enabled: System.get_env("DISABLE_INDEXER") != "true"
config :indexer, Indexer.Tracer, config :indexer, Indexer.Tracer,
service: :indexer, service: :indexer,
adapter: SpandexDatadog.Adapter, adapter: SpandexDatadog.Adapter,

@ -17,11 +17,17 @@ defmodule Indexer.Application do
memory_monitor_name = Memory.Monitor memory_monitor_name = Memory.Monitor
children = [ base_children = [
{Memory.Monitor, [memory_monitor_options, [name: memory_monitor_name]]}, {Memory.Monitor, [memory_monitor_options, [name: memory_monitor_name]]}
{Indexer.Supervisor, [%{memory_monitor: memory_monitor_name}]}
] ]
children =
if Application.get_env(:indexer, Indexer.Supervisor)[:enabled] do
Enum.reverse([{Indexer.Supervisor, [%{memory_monitor: memory_monitor_name}]} | base_children])
else
base_children
end
opts = [ opts = [
# If the `Memory.Monitor` dies, it needs all the `Shrinkable`s to re-register, so restart them. # If the `Memory.Monitor` dies, it needs all the `Shrinkable`s to re-register, so restart them.
strategy: :rest_for_one, strategy: :rest_for_one,

@ -56,3 +56,9 @@ $ export NETWORK=POA
| `SUPPORTED_CHAINS` | | Array of supported chains that displays in the footer and in the chains dropdown. This var was introduced in this PR [#1900](https://github.com/poanetwork/blockscout/pull/1900) and looks like an array of JSON objects. | (empty) | v2.0.0+ | | `SUPPORTED_CHAINS` | | Array of supported chains that displays in the footer and in the chains dropdown. This var was introduced in this PR [#1900](https://github.com/poanetwork/blockscout/pull/1900) and looks like an array of JSON objects. | (empty) | v2.0.0+ |
| `BLOCK_COUNT_CACHE_PERIOD ` | | time to live of cache in seconds. This var was introduced in [#1876](https://github.com/poanetwork/blockscout/pull/1876) | 600 | v2.0.0+ | | `BLOCK_COUNT_CACHE_PERIOD ` | | time to live of cache in seconds. This var was introduced in [#1876](https://github.com/poanetwork/blockscout/pull/1876) | 600 | v2.0.0+ |
| `ALLOWED_EVM_VERSIONS ` | | the comma-separated list of allowed EVM versions for contracts verification. This var was introduced in [#1964](https://github.com/poanetwork/blockscout/pull/1964) | "homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg" | v2.0.0+ | | `ALLOWED_EVM_VERSIONS ` | | the comma-separated list of allowed EVM versions for contracts verification. This var was introduced in [#1964](https://github.com/poanetwork/blockscout/pull/1964) | "homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg" | v2.0.0+ |
| `DISABLE_WEBAPP` | | If `true`, endpoints to webapp are hidden (compile-time) | `false` | master |
| `DISABLE_READ_API` | | If `true`, read-only endpoints to API are hidden (compile-time) | `false` | master |
| `DISABLE_WRITE_API` | | If `true`, write endpoints to API are hidden (compile-time) | `false` | master |
| `DISABLE_INDEXER` | | If `true`, indexer application doesn't run | `false` | master |
| `WEBAPP_URL` | | Link to web application instance, e.g. `http://host/path` | (empty) | master |
| `API_URL` | | Link to API instance, e.g. `http://host/path` | (empty) | master |

@ -2,8 +2,8 @@
"abi": {:hex, :abi, "0.1.12", "87ae04cb09e2308db7b3c350584dc3934de0e308f6a056ba82be5756b081a1ca", [:mix], [{:exth_crypto, "~> 0.1.4", [hex: :exth_crypto, repo: "hexpm", optional: false]}], "hexpm"}, "abi": {:hex, :abi, "0.1.12", "87ae04cb09e2308db7b3c350584dc3934de0e308f6a056ba82be5756b081a1ca", [:mix], [{:exth_crypto, "~> 0.1.4", [hex: :exth_crypto, repo: "hexpm", optional: false]}], "hexpm"},
"abnf2": {:hex, :abnf2, "0.1.2", "6f8792b8ac3288dba5fc889c2bceae9fe78f74e1a7b36bea9726ffaa9d7bef95", [:mix], []}, "abnf2": {:hex, :abnf2, "0.1.2", "6f8792b8ac3288dba5fc889c2bceae9fe78f74e1a7b36bea9726ffaa9d7bef95", [:mix], []},
"absinthe": {:hex, :absinthe, "1.4.14", "fef224a6aac63d6eaafbc0cb96040a8abcd572275b9b4db69d46329acdcae7c7", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "absinthe": {:hex, :absinthe, "1.4.14", "fef224a6aac63d6eaafbc0cb96040a8abcd572275b9b4db69d46329acdcae7c7", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"absinthe_phoenix": {:git, "https://github.com/ayrat555/absinthe_phoenix.git", "4104c7213c328c3698b52710d49cd6d85449305c", [branch: "master"]}, "absinthe_phoenix": {:git, "https://github.com/ayrat555/absinthe_phoenix.git", "0f5127844a9e4e1c5fecb1fcee225894a2af6336", [branch: "master"]},
"absinthe_plug": {:git, "https://github.com/ayrat555/absinthe_plug.git", "e74da0a6e004fe4126885b5f5f60a3e72abd70bb", [branch: "ab-allow-to-set-default-query"]}, "absinthe_plug": {:git, "https://github.com/ayrat555/absinthe_plug.git", "cbe1c170e11e60b3b0146b925a1ce6ec562840ce", [branch: "ab-enable-default-query"]},
"absinthe_relay": {:hex, :absinthe_relay, "1.4.6", "ec0e2288994b388556247cf9601245abec785cdf206d6e844f2992d29de21624", [:mix], [{:absinthe, "~> 1.4.0", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "absinthe_relay": {:hex, :absinthe_relay, "1.4.6", "ec0e2288994b388556247cf9601245abec785cdf206d6e844f2992d29de21624", [:mix], [{:absinthe, "~> 1.4.0", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"accept": {:hex, :accept, "0.3.3", "548ebb6fb2e8b0d170e75bb6123aea6ceecb0189bb1231eeadf52eac08384a97", [:rebar3], [], "hexpm"}, "accept": {:hex, :accept, "0.3.3", "548ebb6fb2e8b0d170e75bb6123aea6ceecb0189bb1231eeadf52eac08384a97", [:rebar3], [], "hexpm"},
"artificery": {:hex, :artificery, "0.2.6", "f602909757263f7897130cbd006b0e40514a541b148d366ad65b89236b93497a", [:mix], [], "hexpm"}, "artificery": {:hex, :artificery, "0.2.6", "f602909757263f7897130cbd006b0e40514a541b148d366ad65b89236b93497a", [:mix], [], "hexpm"},
@ -43,7 +43,7 @@
"ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.6.4", "5b1ac8451f889576bb29dee70412de1170974298727ab944aa4d17e91bdd3472", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.3", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.6.4", "5b1ac8451f889576bb29dee70412de1170974298727ab944aa4d17e91bdd3472", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.3", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ex_cldr_units": {:hex, :ex_cldr_units, "2.5.1", "0e65067a22a7c5146266c313d6333c2700868c32aa6d536f47c6c0d84aac3ac1", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_lists, "~> 2.2", [hex: :ex_cldr_lists, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.6", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ex_cldr_units": {:hex, :ex_cldr_units, "2.5.1", "0e65067a22a7c5146266c313d6333c2700868c32aa6d536f47c6c0d84aac3ac1", [:mix], [{:ex_cldr, "~> 2.6", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_lists, "~> 2.2", [hex: :ex_cldr_lists, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.6", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.19.2", "6f4081ccd9ed081b6dc0bd5af97a41e87f5554de469e7d76025fba535180565f", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.19.2", "6f4081ccd9ed081b6dc0bd5af97a41e87f5554de469e7d76025fba535180565f", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"ex_machina": {:hex, :ex_machina, "2.2.2", "d84217a6fb7840ff771d2561b8aa6d74a0d8968e4b10ecc0d7e9890dc8fb1c6a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm"}, "ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm"},
"ex_rlp": {:hex, :ex_rlp, "0.5.2", "7f4ce7bd55e543c054ce6d49629b01e9833c3462e3d547952be89865f39f2c58", [:mix], [], "hexpm"}, "ex_rlp": {:hex, :ex_rlp, "0.5.2", "7f4ce7bd55e543c054ce6d49629b01e9833c3462e3d547952be89865f39f2c58", [:mix], [], "hexpm"},
"ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm"}, "ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm"},
"exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], []}, "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], []},

Loading…
Cancel
Save