Merge branch 'master' into ab-fix-coin-history-chart

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

@ -1,12 +1,21 @@
## Current
### 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
- [#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
- [#2403](https://github.com/poanetwork/blockscout/pull/2403) - Return gasPrice field at the result of gettxinfo method
### 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
- [#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
- [#2502](https://github.com/poanetwork/blockscout/pull/2502) - increase reward task timeout
- [#2463](https://github.com/poanetwork/blockscout/pull/2463) - dark theme fixes
@ -16,6 +25,7 @@
- [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB
### 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
- [#2402](https://github.com/poanetwork/blockscout/pull/2402) - bump otp version to 22.0

@ -35,5 +35,5 @@
- [ ] 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.
- [ ] 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', () => {
test('sets loading status to false', () => {
const state = Object.assign({}, asyncInitialState, {
loading: true,
loadingFirstPage: true
loading: true
})
const action = { type: 'FINISH_REQUEST' }
const output = asyncReducer(state, action)
expect(output.loading).toEqual(false)
expect(output.loadingFirstPage).toEqual(false)
})
})

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

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

@ -28,7 +28,9 @@ config :block_scout_web,
"EtherChain" => "https://www.etherchain.org/",
"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
@ -85,6 +87,12 @@ config :wobserver,
discovery: :none,
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
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"

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

@ -25,8 +25,9 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
def init(opts), do: opts
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),
true <- action_accessed?(action, write_actions),
{:ok, conn} <- call_controller(conn, controller, action) do
conn
else
@ -64,7 +65,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
end
@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
module_lowercase = String.downcase(module)
@ -83,6 +84,16 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
ArgumentError -> {:error, :no_action}
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
@spec call_controller(Conn.t(), module(), atom()) :: {:ok, Conn.t()} | {:error, :no_action} | {:error, Exception.t()}
defp call_controller(conn, controller, action) do

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

@ -2,6 +2,7 @@ defmodule BlockScoutWeb.Router do
use BlockScoutWeb, :router
alias BlockScoutWeb.Plug.GraphQL
alias BlockScoutWeb.{ApiRouter, WebRouter}
forward("/wobserver", Wobserver.Web.Router)
forward("/admin", BlockScoutWeb.AdminRouter)
@ -18,249 +19,54 @@ defmodule BlockScoutWeb.Router do
plug(:accepts, ["json"])
end
scope "/api/v1", BlockScoutWeb.API.V1, as: :api_v1 do
pipe_through(:api)
forward("/api", ApiRouter)
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])
resources("/verified_smart_contracts", VerifiedSmartContractController, only: [:create])
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
)
else
scope "/", BlockScoutWeb do
pipe_through(:browser)
get("/api_docs", PageNotFoundController, :index)
get("/eth_rpc_api_docs", PageNotFoundController, :index)
end
end
scope "/verify_smart_contract" do
pipe_through(:api)
scope "/", BlockScoutWeb do
pipe_through(:browser)
post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create)
get("/api_docs", APIDocsController, :index)
get("/eth_rpc_api_docs", APIDocsController, :eth_rpc)
end
scope "/api", BlockScoutWeb.API.RPC do
scope "/verify_smart_contract" do
pipe_through(:api)
alias BlockScoutWeb.API.RPC
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)
post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create)
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
)
if Application.get_env(:block_scout_web, WebRouter)[:enabled] do
forward("/", BlockScoutWeb.WebRouter)
else
scope "/", BlockScoutWeb do
pipe_through(:browser)
resources "/blocks", BlockController, only: [:index, :show], param: "hash_or_number" do
resources("/transactions", BlockTransactionController, only: [:index], as: :transaction)
forward("/", APIDocsController, :index)
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

@ -14,15 +14,14 @@
<!-- Pagination -->
<ul class="pagination">
<!-- First -->
<%= if assigns[:first_page_path] do %>
<li class="page-item">
<a
<%= if !assigns[:first_page_path] do %>disabled<% end %>
class="page-link"
href='<%= "#{assigns[:first_page_path]}" %>'
>First</a>
</li>
<% end %>
<li class="page-item">
<a
disabled
class="page-link"
href
data-first-page-button
>First</a>
</li>
<!-- Previous -->
<li class="page-item">
<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="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">
<span class="tile-label">
@ -22,7 +22,7 @@
</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="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">
<span class="tile-label">
@ -46,7 +46,7 @@
</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="tile-transaction-type-block col-md-2 d-flex flex-row flex-md-column">
<span class="tile-label">

@ -3,7 +3,7 @@
<!-- Logo -->
<div class="row footer-logo-row">
<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() %>" />
<% end %>
</div>

@ -9,7 +9,7 @@
}
</script>
<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() %>" />
<% end %>
<script>
@ -22,79 +22,83 @@
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link topnav-nav-link dropdown-toggle" href="#" id="navbarBlocksDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_block_icon.html" %>
</span>
<%= 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 %>
<%= if Application.get_env(:block_scout_web, BlockScoutWeb.WebRouter)[:enabled] do %>
<li class="nav-item dropdown">
<a class="nav-link topnav-nav-link dropdown-toggle" href="#" id="navbarBlocksDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_block_icon.html" %>
</span>
<%= 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 %>
<%= 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 %>
</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>
<% end %>
<%= if Application.get_env(:block_scout_web, BlockScoutWeb.ApiRouter)[:reading_enabled] || Application.get_env(:block_scout_web, :api_url) do %>
<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: api_url() <> "/graphiql"
) %>
<%= link(
gettext("RPC"),
class: "dropdown-item #{tab_status("api_docs", @conn.request_path)}",
to: api_url() <> api_docs_path(@conn, :index)
) %>
<%= link(
gettext("Eth RPC"),
class: "dropdown-item #{tab_status("eth_rpc_api_docs", @conn.request_path)}",
to: api_url() <> api_docs_path(@conn, :eth_rpc)
) %>
</div>
</li>
<% end %>
<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">
<span class="nav-link-icon">
@ -111,40 +115,42 @@
</svg>
</button>
<!-- Search navbar -->
<div class="search-form d-lg-flex d-inline-block" style="background-color: #22223a">
<%= form_for @conn, chain_path(@conn, :search), [class: "form-inline my-2 my-lg-0", method: :get, enforce_utf8: false], fn f -> %>
<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"),
"aria-describedby": "search-icon",
"aria-label": gettext("Search"),
"data-test": "search_input"
],
[ url: "#{chain_path(@conn, :token_autocomplete)}?q=",
limit: 0,
minChars: 2,
value: "contract_address_hash",
label: "contract_address_hash",
descrSearch: true,
descr: "symbol"
]) %>
<div class="input-group-append">
<button class="input-group-text" id="search-icon">
<%= render BlockScoutWeb.IconsView, "_search_icon.html" %>
</button>
</div>
</div>
<button class="btn btn-outline-success my-2 my-sm-0 sr-only hidden" type="submit"><%= gettext "Search" %></button>
<script>
if (localStorage.getItem("current-color-mode") === "dark") {
document.getElementById("q").style.backgroundColor = "#22223a";
document.getElementById("q").style.borderColor = "#22223a";
}
</script>
<% end %>
</div>
<%= if Application.get_env(:block_scout_web, BlockScoutWeb.WebRouter)[:enabled] do %>
<div class="search-form d-lg-flex d-inline-block" style="background-color: #22223a">
<%= form_for @conn, chain_path(@conn, :search), [class: "form-inline my-2 my-lg-0", method: :get, enforce_utf8: false], fn f -> %>
<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"),
"aria-describedby": "search-icon",
"aria-label": gettext("Search"),
"data-test": "search_input"
],
[ url: "#{chain_path(@conn, :token_autocomplete)}?q=",
limit: 0,
minChars: 2,
value: "contract_address_hash",
label: "contract_address_hash",
descrSearch: true,
descr: "symbol"
]) %>
<div class="input-group-append">
<button class="input-group-text" id="search-icon">
<%= render BlockScoutWeb.IconsView, "_search_icon.html" %>
</button>
</div>
</div>
<button class="btn btn-outline-success my-2 my-sm-0 sr-only hidden" type="submit"><%= gettext "Search" %></button>
<script>
if (localStorage.getItem("current-color-mode") === "dark") {
document.getElementById("q").style.backgroundColor = "#22223a";
document.getElementById("q").style.borderColor = "#22223a";
}
</script>
<% end %>
</div>
<% end %>
</div>
</div>
</nav>

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

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

@ -228,4 +228,33 @@ defmodule BlockScoutWeb.LayoutView do
[]
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

@ -40,22 +40,43 @@ defmodule BlockScoutWeb.TransactionView do
end
def aggregate_token_transfers(token_transfers) do
token_transfers
|> Enum.reduce(%{}, fn token_transfer, acc ->
new_entry = %{
token: token_transfer.token,
amount: token_transfer.amount,
token_id: token_transfer.token_id
}
{transfers, nft_transfers} =
token_transfers
|> Enum.reduce({%{}, []}, fn token_transfer, acc ->
aggregate_reducer(token_transfer, acc)
end)
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
| amount: Decimal.add(new_entry.amount, existing_entry.amount)
})
end)
|> Enum.map(fn {_key, value} -> value end)
{new_acc1, acc2}
end
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

@ -102,7 +102,7 @@ defmodule BlockScoutWeb.Mixfile do
{:phoenix, "~> 1.4"},
{:phoenix_ecto, "~> 4.0"},
{:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: [:dev]},
{:phoenix_live_reload, "~> 1.2", only: [:dev]},
{:phoenix_pubsub, "~> 1.0"},
# use `:cowboy` for WebServer with `:plug`
{:plug_cowboy, "~> 2.0"},

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

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

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressContractControllerTest do
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.ExchangeRates.Token

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

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressTokenControllerTest do
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}

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

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressTransactionControllerTest do
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.ExchangeRates.Token

@ -37,7 +37,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do
test "with a bad action atom", %{conn: conn} do
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 response = json_response(result, 400)
assert response["message"] =~ "Unknown action"
@ -49,7 +49,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do
test "with an invalid controller action", %{conn: conn} do
conn = %Conn{conn | params: %{"module" => "test", "action" => "index"}}
result = RPCTranslator.call(conn, %{"test" => TestController})
result = RPCTranslator.call(conn, %{"test" => {TestController, []}})
assert result.halted
assert response = json_response(result, 400)
assert response["message"] =~ "Unknown action"
@ -59,7 +59,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do
end
test "with missing params", %{conn: conn} do
result = RPCTranslator.call(conn, %{"test" => TestController})
result = RPCTranslator.call(conn, %{"test" => {TestController, []}})
assert result.halted
assert response = json_response(result, 400)
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
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) == %{}
end
end

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

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

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

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

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

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.BlockTransactionControllerTest do
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
test "with invalid block number", %{conn: conn} do

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.ChainControllerTest do
# ETS table is shared in `Explorer.Counters.AddressesWithBalanceCounter`
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.Counters.AddressesWithBalanceCounter

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.PendingTransactionControllerTest do
use BlockScoutWeb.ConnCase
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
test "returns no transactions that are in a block", %{conn: conn} do

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.RecentTransactionsControllerTest do
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

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.TransactionControllerTest do
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]
alias Explorer.Chain.Transaction

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do
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.ExchangeRates.Token

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.TransactionLogControllerTest do
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

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do
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

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.TransactionLogsPage do
use Wallaby.DSL
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 BlockScoutWeb.Endpoint

@ -268,5 +268,19 @@ defmodule BlockScoutWeb.TransactionViewTest do
assert Enum.count(result) == 1
assert List.first(result).amount == Decimal.new(3)
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

@ -20,6 +20,7 @@ defmodule BlockScoutWeb.ConnCase do
# Import conveniences for testing with connections
use Phoenix.ConnTest
import BlockScoutWeb.Router.Helpers
import BlockScoutWeb.WebRouter.Helpers, except: [static_path: 2]
# The default endpoint for testing
@endpoint BlockScoutWeb.Endpoint
@ -27,6 +28,7 @@ defmodule BlockScoutWeb.ConnCase do
import Explorer.Factory
alias BlockScoutWeb.AdminRouter.Helpers, as: AdminRoutes
alias BlockScoutWeb.ApiRouter.Helpers, as: ApiRoutes
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.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]
@ -65,7 +65,7 @@ if System.get_env("METADATA_CONTRACT") && System.get_env("VALIDATORS_CONTRACT")
metadata_contract_address: System.get_env("METADATA_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
config :explorer, Explorer.Validator.MetadataProcessor, enabled: false
end

@ -73,10 +73,12 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
# order so that row ShareLocks are grabbed in a consistent order
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} =
Import.insert_changes_list(
repo,
ordered_changes_list,
final_changes_list,
conflict_target: [:transaction_hash, :index],
for: InternalTransaction,
on_conflict: on_conflict,
@ -156,6 +158,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
from(
t in Transaction,
where: t.hash in ^ordered_transaction_hashes,
where: not is_nil(t.block_hash),
update: [
set: [
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
{^transaction_count, result} = repo.update_all(query, [], timeout: timeout)
{_transaction_count, result} = repo.update_all(query, [], timeout: timeout)
{:ok, result}
rescue
@ -191,4 +192,23 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
{:error, %{exception: postgrex_error, transaction_hashes: ordered_transaction_hashes}}
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

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

@ -2,7 +2,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do
use Explorer.DataCase
alias Ecto.Multi
alias Explorer.Chain.{Data, Wei, Transaction}
alias Explorer.Chain.{Data, Wei, Transaction, InternalTransaction}
alias Explorer.Chain.Import.Runner.InternalTransactions
describe "run/1" do
@ -20,6 +20,28 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do
assert :error == Repo.get(Transaction, transaction.hash).status
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
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.StakingPools.Supervisor, disabled?: true
config :indexer, Indexer.Supervisor, enabled: System.get_env("DISABLE_INDEXER") != "true"
config :indexer, Indexer.Tracer,
service: :indexer,
adapter: SpandexDatadog.Adapter,

@ -17,11 +17,17 @@ defmodule Indexer.Application do
memory_monitor_name = Memory.Monitor
children = [
{Memory.Monitor, [memory_monitor_options, [name: memory_monitor_name]]},
{Indexer.Supervisor, [%{memory_monitor: memory_monitor_name}]}
base_children = [
{Memory.Monitor, [memory_monitor_options, [name: 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 = [
# If the `Memory.Monitor` dies, it needs all the `Shrinkable`s to re-register, so restart them.
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+ |
| `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+ |
| `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 |

@ -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_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_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_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm"},
"exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], []},

Loading…
Cancel
Save