Merge branch 'loader' of github.com:poanetwork/blockscout into loader

pull/2405/head
Victor Baranov 5 years ago
commit 7205074f6b
  1. 8
      CHANGELOG.md
  2. 6
      apps/block_scout_web/assets/js/lib/market_history_chart.js
  3. 13
      apps/block_scout_web/assets/js/lib/utils.js
  4. 10
      apps/block_scout_web/assets/js/pages/chain.js
  5. 6
      apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex
  6. 12
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex
  7. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex
  8. 12
      apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex
  9. 5
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/block_controller.ex
  10. 2
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex
  11. 21
      apps/block_scout_web/lib/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller.ex
  12. 21
      apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex
  13. 4
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  14. 12
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  15. 4
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex
  16. 6
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex
  17. 8
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex
  18. 6
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex
  19. 2
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex
  20. 9
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex
  21. 2
      apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex
  22. 18
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  23. 4
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  24. 38
      apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex
  25. 36
      apps/block_scout_web/priv/gettext/default.pot
  26. 36
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  27. 18
      apps/explorer/config/config.exs
  28. 294
      apps/explorer/lib/explorer/chain.ex
  29. 2
      apps/explorer/lib/explorer/counters/average_block_time.ex
  30. 2
      apps/explorer/lib/explorer/market/market_history_cache.ex
  31. 82
      apps/explorer/lib/explorer/smart_contract/reader.ex
  32. 70
      apps/explorer/test/explorer/chain_test.exs
  33. 2
      apps/explorer/test/explorer/smart_contract/reader_test.exs

@ -1,13 +1,17 @@
## Current ## Current
### Features ### Features
- [#2366](https://github.com/poanetwork/blockscout/pull/2366) - paginate eth logs - [#2391](https://github.com/poanetwork/blockscout/pull/2391) - Controllers Improvements
- [#2379](https://github.com/poanetwork/blockscout/pull/2379) - Disable network selector when is empty - [#2379](https://github.com/poanetwork/blockscout/pull/2379) - Disable network selector when is empty
- [#2374](https://github.com/poanetwork/blockscout/pull/2374) - decode constructor arguments for verified smart contracts
- [#2366](https://github.com/poanetwork/blockscout/pull/2366) - paginate eth logs
- [#2360](https://github.com/poanetwork/blockscout/pull/2360) - add default evm version to smart contract verification - [#2360](https://github.com/poanetwork/blockscout/pull/2360) - add default evm version to smart contract verification
- [#2352](https://github.com/poanetwork/blockscout/pull/2352) - Fetch rewards in parallel with transactions - [#2352](https://github.com/poanetwork/blockscout/pull/2352) - Fetch rewards in parallel with transactions
- [#2294](https://github.com/poanetwork/blockscout/pull/2294) - add healthy block period checking endpoint - [#2294](https://github.com/poanetwork/blockscout/pull/2294) - add healthy block period checking endpoint
- [#2324](https://github.com/poanetwork/blockscout/pull/2324) - set timeout for loading message on the main page
### Fixes ### Fixes
- [#2410](https://github.com/poanetwork/blockscout/pull/2410) - preload smart contract for logs decoding
- [#2405](https://github.com/poanetwork/blockscout/pull/2405) - added templates for table loader and tile loader - [#2405](https://github.com/poanetwork/blockscout/pull/2405) - added templates for table loader and tile loader
- [#2398](https://github.com/poanetwork/blockscout/pull/2398) - show only one decoded candidate - [#2398](https://github.com/poanetwork/blockscout/pull/2398) - show only one decoded candidate
- [#2395](https://github.com/poanetwork/blockscout/pull/2395) - new block loading animation - [#2395](https://github.com/poanetwork/blockscout/pull/2395) - new block loading animation
@ -29,6 +33,8 @@
- [#2326](https://github.com/poanetwork/blockscout/pull/2326) - fix nested constructor arguments - [#2326](https://github.com/poanetwork/blockscout/pull/2326) - fix nested constructor arguments
### Chore ### Chore
- [#2418](https://github.com/poanetwork/blockscout/pull/2418) - Remove parentheses in market cap percentage
- [#2401](https://github.com/poanetwork/blockscout/pull/2401) - add ENV vars to manage updating period of average block time and market history cache
- [#2363](https://github.com/poanetwork/blockscout/pull/2363) - add parameters example for eth rpc - [#2363](https://github.com/poanetwork/blockscout/pull/2363) - add parameters example for eth rpc
- [#2342](https://github.com/poanetwork/blockscout/pull/2342) - Upgrade Postgres image version in Docker setup - [#2342](https://github.com/poanetwork/blockscout/pull/2342) - Upgrade Postgres image version in Docker setup
- [#2325](https://github.com/poanetwork/blockscout/pull/2325) - Reduce function input to address' hash only where possible - [#2325](https://github.com/poanetwork/blockscout/pull/2325) - Reduce function input to address' hash only where possible

@ -4,6 +4,7 @@ import humps from 'humps'
import numeral from 'numeral' import numeral from 'numeral'
import { formatUsdValue } from '../lib/currency' import { formatUsdValue } from '../lib/currency'
import sassVariables from '../../css/app.scss' import sassVariables from '../../css/app.scss'
import { showLoader } from '../lib/utils'
const config = { const config = {
type: 'line', type: 'line',
@ -129,6 +130,10 @@ class MarketHistoryChart {
export function createMarketHistoryChart (el) { export function createMarketHistoryChart (el) {
const dataPath = el.dataset.market_history_chart_path const dataPath = el.dataset.market_history_chart_path
const $chartLoading = $('[data-chart-loading-message]') const $chartLoading = $('[data-chart-loading-message]')
const isTimeout = true
const timeoutID = showLoader(isTimeout, $chartLoading)
const $chartError = $('[data-chart-error-message]') const $chartError = $('[data-chart-error-message]')
const chart = new MarketHistoryChart(el, 0, []) const chart = new MarketHistoryChart(el, 0, [])
$.getJSON(dataPath, {type: 'JSON'}) $.getJSON(dataPath, {type: 'JSON'})
@ -143,6 +148,7 @@ export function createMarketHistoryChart (el) {
}) })
.always(() => { .always(() => {
$chartLoading.hide() $chartLoading.hide()
clearTimeout(timeoutID)
}) })
return chart return chart
} }

@ -11,3 +11,16 @@ export function batchChannel (func) {
debouncedFunc() debouncedFunc()
} }
} }
export function showLoader (isTimeout, loader) {
if (isTimeout) {
const timeout = setTimeout(function () {
loader.removeAttr('hidden')
loader.show()
}, 1000)
return timeout
} else {
loader.hide()
return null
}
}

@ -9,7 +9,7 @@ import numeral from 'numeral'
import socket from '../socket' import socket from '../socket'
import { exchangeRateChannel, formatUsdValue } from '../lib/currency' import { exchangeRateChannel, formatUsdValue } from '../lib/currency'
import { createStore, connectElements } from '../lib/redux_helpers.js' import { createStore, connectElements } from '../lib/redux_helpers.js'
import { batchChannel } from '../lib/utils' import { batchChannel, showLoader } from '../lib/utils'
import listMorph from '../lib/list_morph' import listMorph from '../lib/list_morph'
import { createMarketHistoryChart } from '../lib/market_history_chart' import { createMarketHistoryChart } from '../lib/market_history_chart'
@ -214,11 +214,7 @@ const elements = {
}, },
'[data-selector="chain-block-list"] [data-selector="loading-message"]': { '[data-selector="chain-block-list"] [data-selector="loading-message"]': {
render ($el, state, oldState) { render ($el, state, oldState) {
if (state.blocksLoading) { showLoader(state.blocksLoading, $el)
$el.show()
} else {
$el.hide()
}
} }
}, },
'[data-selector="transactions-list"] [data-selector="error-message"]': { '[data-selector="transactions-list"] [data-selector="error-message"]': {
@ -228,7 +224,7 @@ const elements = {
}, },
'[data-selector="transactions-list"] [data-selector="loading-message"]': { '[data-selector="transactions-list"] [data-selector="loading-message"]': {
render ($el, state, oldState) { render ($el, state, oldState) {
$el.toggle(state.transactionsLoading) showLoader(state.transactionsLoading, $el)
} }
}, },
'[data-selector="transactions-list"]': { '[data-selector="transactions-list"]': {

@ -16,7 +16,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash, [], false) do :ok <- Chain.check_address_exists(address_hash) do
full_options = paging_options(params) full_options = paging_options(params)
coin_balances_plus_one = Chain.address_to_coin_balances(address_hash, full_options) coin_balances_plus_one = Chain.address_to_coin_balances(address_hash, full_options)
@ -32,7 +32,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
address_coin_balance_path( address_coin_balance_path(
conn, conn,
:index, :index,
address, address_hash,
Map.delete(next_page_params, "type") Map.delete(next_page_params, "type")
) )
end end
@ -52,7 +52,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
:error -> :error ->
unprocessable_entity(conn) unprocessable_entity(conn)
{:error, :not_found} -> :not_found ->
not_found(conn) not_found(conn)
end end
end end

@ -8,8 +8,18 @@ defmodule BlockScoutWeb.AddressContractController do
alias Indexer.Fetcher.CoinBalanceOnDemand alias Indexer.Fetcher.CoinBalanceOnDemand
def index(conn, %{"address_id" => address_hash_string}) do def index(conn, %{"address_id" => address_hash_string}) do
address_options = [
necessity_by_association: %{
:contracts_creation_internal_transaction => :optional,
:names => :optional,
:smart_contract => :optional,
:token => :optional,
:contracts_creation_transaction => :optional
}
]
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_contract_address(address_hash) do {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do
render( render(
conn, conn,
"index.html", "index.html",

@ -16,7 +16,7 @@ defmodule BlockScoutWeb.AddressLogsController do
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash, [], false) do :ok <- Chain.check_address_exists(address_hash) do
logs_plus_one = Chain.address_to_logs(address_hash, paging_options(params)) logs_plus_one = Chain.address_to_logs(address_hash, paging_options(params))
{results, next_page} = split_list_by_page(logs_plus_one) {results, next_page} = split_list_by_page(logs_plus_one)
@ -26,7 +26,7 @@ defmodule BlockScoutWeb.AddressLogsController do
nil nil
next_page_params -> next_page_params ->
address_logs_path(conn, :index, address, Map.delete(next_page_params, "type")) address_logs_path(conn, :index, address_hash, Map.delete(next_page_params, "type"))
end end
items = items =
@ -74,7 +74,7 @@ defmodule BlockScoutWeb.AddressLogsController do
def search_logs(conn, %{"topic" => topic, "address_id" => address_hash_string} = params) do def search_logs(conn, %{"topic" => topic, "address_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash, [], false) do :ok <- Chain.check_address_exists(address_hash) do
topic = String.trim(topic) topic = String.trim(topic)
formatted_topic = if String.starts_with?(topic, "0x"), do: topic, else: "0x" <> topic formatted_topic = if String.starts_with?(topic, "0x"), do: topic, else: "0x" <> topic
@ -89,7 +89,7 @@ defmodule BlockScoutWeb.AddressLogsController do
nil nil
next_page_params -> next_page_params ->
address_logs_path(conn, :index, address, Map.delete(next_page_params, "type")) address_logs_path(conn, :index, address_hash, Map.delete(next_page_params, "type"))
end end
items = items =

@ -15,8 +15,18 @@ defmodule BlockScoutWeb.AddressReadContractController do
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
def index(conn, %{"address_id" => address_hash_string}) do def index(conn, %{"address_id" => address_hash_string}) do
address_options = [
necessity_by_association: %{
:contracts_creation_internal_transaction => :optional,
:names => :optional,
:smart_contract => :optional,
:token => :optional,
:contracts_creation_transaction => :optional
}
]
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_contract_address(address_hash) do {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do
render( render(
conn, conn,
"index.html", "index.html",

@ -8,9 +8,8 @@ defmodule BlockScoutWeb.API.RPC.BlockController do
def getblockreward(conn, params) do def getblockreward(conn, params) do
with {:block_param, {:ok, unsafe_block_number}} <- {:block_param, Map.fetch(params, "blockno")}, with {:block_param, {:ok, unsafe_block_number}} <- {:block_param, Map.fetch(params, "blockno")},
{:ok, block_number} <- ChainWeb.param_to_block_number(unsafe_block_number), {:ok, block_number} <- ChainWeb.param_to_block_number(unsafe_block_number),
block_options = [necessity_by_association: %{transactions: :optional}], {:ok, block} <- Chain.number_to_block(block_number) do
{:ok, block} <- Chain.number_to_block(block_number, block_options) do reward = Chain.block_reward(block_number)
reward = Chain.block_reward(block)
render(conn, :block_reward, block: block, reward: reward) render(conn, :block_reward, block: block, reward: reward)
else else

@ -10,7 +10,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do
{:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param), {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param),
{:transaction, {:ok, transaction}} <- transaction_from_hash(transaction_hash), {:transaction, {:ok, transaction}} <- transaction_from_hash(transaction_hash),
paging_options <- paging_options(params) do paging_options <- paging_options(params) do
logs = Chain.transaction_to_logs(transaction, paging_options) logs = Chain.transaction_to_logs(transaction_hash, paging_options)
{logs, next_page} = split_list_by_page(logs) {logs, next_page} = split_list_by_page(logs)
render(conn, :gettxinfo, %{ render(conn, :gettxinfo, %{

@ -7,8 +7,9 @@ defmodule BlockScoutWeb.API.V1.DecompiledSmartContractController do
def create(conn, params) do def create(conn, params) do
if auth_token(conn) == actual_token() do if auth_token(conn) == actual_token() do
with {:ok, hash} <- validate_address_hash(params["address_hash"]), with {:ok, hash} <- validate_address_hash(params["address_hash"]),
:ok <- smart_contract_exists?(hash), :ok <- Chain.check_address_exists(hash),
:ok <- decompiled_contract_exists?(params["address_hash"], params["decompiler_version"]) do {:contract, :not_found} <-
{:contract, Chain.check_decompiled_contract_exists(params["address_hash"], params["decompiler_version"])} do
case Chain.create_decompiled_smart_contract(params) do case Chain.create_decompiled_smart_contract(params) do
{:ok, decompiled_smart_contract} -> {:ok, decompiled_smart_contract} ->
send_resp(conn, :created, Jason.encode!(decompiled_smart_contract)) send_resp(conn, :created, Jason.encode!(decompiled_smart_contract))
@ -29,7 +30,7 @@ defmodule BlockScoutWeb.API.V1.DecompiledSmartContractController do
:not_found -> :not_found ->
send_resp(conn, :unprocessable_entity, encode(%{error: "address is not found"})) send_resp(conn, :unprocessable_entity, encode(%{error: "address is not found"}))
:contract_exists -> {:contract, :ok} ->
send_resp( send_resp(
conn, conn,
:unprocessable_entity, :unprocessable_entity,
@ -41,13 +42,6 @@ defmodule BlockScoutWeb.API.V1.DecompiledSmartContractController do
end end
end end
defp smart_contract_exists?(address_hash) do
case Chain.hash_to_address(address_hash) do
{:ok, _address} -> :ok
_ -> :not_found
end
end
defp validate_address_hash(address_hash) do defp validate_address_hash(address_hash) do
case Address.cast(address_hash) do case Address.cast(address_hash) do
{:ok, hash} -> {:ok, hash} {:ok, hash} -> {:ok, hash}
@ -55,13 +49,6 @@ defmodule BlockScoutWeb.API.V1.DecompiledSmartContractController do
end end
end end
defp decompiled_contract_exists?(address_hash, decompiler_version) do
case Chain.decompiled_code(address_hash, decompiler_version) do
{:ok, _} -> :contract_exists
_ -> :ok
end
end
defp auth_token(conn) do defp auth_token(conn) do
case get_req_header(conn, "auth_token") do case get_req_header(conn, "auth_token") do
[token] -> token [token] -> token

@ -7,8 +7,8 @@ defmodule BlockScoutWeb.API.V1.VerifiedSmartContractController do
def create(conn, params) do def create(conn, params) do
with {:ok, hash} <- validate_address_hash(params["address_hash"]), with {:ok, hash} <- validate_address_hash(params["address_hash"]),
:ok <- smart_contract_exists?(hash), :ok <- Chain.check_address_exists(hash),
:ok <- verified_smart_contract_exists?(hash) do {:contract, :not_found} <- {:contract, Chain.check_verified_smart_contract_exists(hash)} do
external_libraries = fetch_external_libraries(params) external_libraries = fetch_external_libraries(params)
case Publisher.publish(hash, params, external_libraries) do case Publisher.publish(hash, params, external_libraries) do
@ -31,7 +31,7 @@ defmodule BlockScoutWeb.API.V1.VerifiedSmartContractController do
:not_found -> :not_found ->
send_resp(conn, :unprocessable_entity, encode(%{error: "address is not found"})) send_resp(conn, :unprocessable_entity, encode(%{error: "address is not found"}))
:contract_exists -> {:contract, :ok} ->
send_resp( send_resp(
conn, conn,
:unprocessable_entity, :unprocessable_entity,
@ -40,13 +40,6 @@ defmodule BlockScoutWeb.API.V1.VerifiedSmartContractController do
end end
end end
defp smart_contract_exists?(address_hash) do
case Chain.hash_to_address(address_hash) do
{:ok, _address} -> :ok
_ -> :not_found
end
end
defp validate_address_hash(address_hash) do defp validate_address_hash(address_hash) do
case Address.cast(address_hash) do case Address.cast(address_hash) do
{:ok, hash} -> {:ok, hash} {:ok, hash} -> {:ok, hash}
@ -54,14 +47,6 @@ defmodule BlockScoutWeb.API.V1.VerifiedSmartContractController do
end end
end end
defp verified_smart_contract_exists?(address_hash) do
if Chain.address_hash_to_smart_contract(address_hash) do
:contract_exists
else
:ok
end
end
defp encode(data) do defp encode(data) do
Jason.encode!(data) Jason.encode!(data)
end end

@ -26,7 +26,7 @@ defmodule BlockScoutWeb.BlockTransactionController do
paging_options(params) paging_options(params)
) )
transactions_plus_one = Chain.block_to_transactions(block, full_options) transactions_plus_one = Chain.block_to_transactions(block.hash, full_options)
{transactions, next_page} = split_list_by_page(transactions_plus_one) {transactions, next_page} = split_list_by_page(transactions_plus_one)
@ -89,7 +89,7 @@ defmodule BlockScoutWeb.BlockTransactionController do
:rewards => :optional :rewards => :optional
} }
) do ) do
block_transaction_count = Chain.block_to_transaction_count(block) block_transaction_count = Chain.block_to_transaction_count(block.hash)
render( render(
conn, conn,

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.ChainController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
alias BlockScoutWeb.ChainView alias BlockScoutWeb.ChainView
alias Explorer.{Chain, PagingOptions, Repo} alias Explorer.{Chain, PagingOptions}
alias Explorer.Chain.{Address, Block, Transaction} alias Explorer.Chain.{Address, Block, Transaction}
alias Explorer.Chain.Supply.RSK alias Explorer.Chain.Supply.RSK
alias Explorer.Counters.AverageBlockTime alias Explorer.Counters.AverageBlockTime
@ -72,9 +72,15 @@ defmodule BlockScoutWeb.ChainController do
def chain_blocks(conn, _params) do def chain_blocks(conn, _params) do
if ajax?(conn) do if ajax?(conn) do
blocks = blocks =
[paging_options: %PagingOptions{page_size: 4}] [
paging_options: %PagingOptions{page_size: 4},
necessity_by_association: %{
[miner: :names] => :optional,
:transactions => :optional,
:rewards => :optional
}
]
|> Chain.list_blocks() |> Chain.list_blocks()
|> Repo.preload([[miner: :names], :transactions, :rewards])
|> Enum.map(fn block -> |> Enum.map(fn block ->
%{ %{
chain_block_html: chain_block_html:

@ -33,7 +33,7 @@ defmodule BlockScoutWeb.SmartContractController do
def show(conn, params) do def show(conn, params) do
with true <- ajax?(conn), with true <- ajax?(conn),
{:ok, address_hash} <- Chain.string_to_address_hash(params["id"]), {:ok, address_hash} <- Chain.string_to_address_hash(params["id"]),
{:ok, _address} <- Chain.find_contract_address(address_hash), :ok <- Chain.check_contract_address_exists(address_hash),
outputs = outputs =
Reader.query_function( Reader.query_function(
address_hash, address_hash,
@ -51,7 +51,7 @@ defmodule BlockScoutWeb.SmartContractController do
:error -> :error ->
unprocessable_entity(conn) unprocessable_entity(conn)
{:error, :not_found} -> :not_found ->
not_found(conn) not_found(conn)
_ -> _ ->

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

@ -10,7 +10,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
def index(conn, %{"transaction_id" => hash_string, "type" => "JSON"} = params) do def index(conn, %{"transaction_id" => hash_string, "type" => "JSON"} = params) do
with {:ok, hash} <- Chain.string_to_transaction_hash(hash_string), with {:ok, hash} <- Chain.string_to_transaction_hash(hash_string),
{:ok, transaction} <- Chain.hash_to_transaction(hash) do :ok <- Chain.check_transaction_exists(hash) do
full_options = full_options =
Keyword.merge( Keyword.merge(
[ [
@ -24,7 +24,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
paging_options(params) paging_options(params)
) )
internal_transactions_plus_one = Chain.transaction_to_internal_transactions(transaction, full_options) internal_transactions_plus_one = Chain.transaction_to_internal_transactions(hash, full_options)
{internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one) {internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one)
@ -37,7 +37,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
transaction_internal_transaction_path( transaction_internal_transaction_path(
conn, conn,
:index, :index,
transaction, hash,
Map.delete(next_page_params, "type") Map.delete(next_page_params, "type")
) )
end end
@ -66,7 +66,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
|> put_view(TransactionView) |> put_view(TransactionView)
|> render("invalid.html", transaction_hash: hash_string) |> render("invalid.html", transaction_hash: hash_string)
{:error, :not_found} -> :not_found ->
conn conn
|> put_status(404) |> put_status(404)
|> put_view(TransactionView) |> put_view(TransactionView)

@ -11,7 +11,9 @@ defmodule BlockScoutWeb.TransactionLogController do
def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do
with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string),
{:ok, transaction} <- {:ok, transaction} <-
Chain.hash_to_transaction(transaction_hash) do Chain.hash_to_transaction(transaction_hash,
necessity_by_association: %{[to_address: :smart_contract] => :optional}
) do
full_options = full_options =
Keyword.merge( Keyword.merge(
[ [
@ -22,7 +24,7 @@ defmodule BlockScoutWeb.TransactionLogController do
paging_options(params) paging_options(params)
) )
logs_plus_one = Chain.transaction_to_logs(transaction, full_options) logs_plus_one = Chain.transaction_to_logs(transaction_hash, full_options)
{logs, next_page} = split_list_by_page(logs_plus_one) {logs, next_page} = split_list_by_page(logs_plus_one)

@ -19,7 +19,7 @@ defmodule BlockScoutWeb.TransactionRawTraceController do
:token_transfers => :optional :token_transfers => :optional
} }
) do ) do
internal_transactions = Chain.transaction_to_internal_transactions(transaction) internal_transactions = Chain.transaction_to_internal_transactions(hash)
render( render(
conn, conn,

@ -10,8 +10,7 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do
def index(conn, %{"transaction_id" => hash_string, "type" => "JSON"} = params) do def index(conn, %{"transaction_id" => hash_string, "type" => "JSON"} = params) do
with {:ok, hash} <- Chain.string_to_transaction_hash(hash_string), with {:ok, hash} <- Chain.string_to_transaction_hash(hash_string),
{:ok, transaction} <- :ok <- Chain.check_transaction_exists(hash) do
Chain.hash_to_transaction(hash) do
full_options = full_options =
Keyword.merge( Keyword.merge(
[ [
@ -24,7 +23,7 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do
paging_options(params) paging_options(params)
) )
token_transfers_plus_one = Chain.transaction_to_token_transfers(transaction, full_options) token_transfers_plus_one = Chain.transaction_to_token_transfers(hash, full_options)
{token_transfers, next_page} = split_list_by_page(token_transfers_plus_one) {token_transfers, next_page} = split_list_by_page(token_transfers_plus_one)
@ -34,7 +33,7 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do
nil nil
next_page_params -> next_page_params ->
transaction_token_transfer_path(conn, :index, transaction, Map.delete(next_page_params, "type")) transaction_token_transfer_path(conn, :index, hash, Map.delete(next_page_params, "type"))
end end
items = items =
@ -62,7 +61,7 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do
|> put_view(TransactionView) |> put_view(TransactionView)
|> render("invalid.html", transaction_hash: hash_string) |> render("invalid.html", transaction_hash: hash_string)
{:error, :not_found} -> :not_found ->
conn conn
|> put_status(404) |> put_status(404)
|> put_view(TransactionView) |> put_view(TransactionView)

@ -21,7 +21,7 @@
<td class="stakes-td color-lighten"> <td class="stakes-td color-lighten">
<!-- percentage of coins from total supply --> <!-- percentage of coins from total supply -->
<%= if @total_supply do %> <%= if @total_supply do %>
(<%= balance_percentage(@address, @total_supply) %>) <%= balance_percentage(@address, @total_supply) %>
<% end %> <% end %>
</td> </td>
<td class="stakes-td"> <td class="stakes-td">

@ -46,14 +46,18 @@
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.evm_version %></dd> <dd class="col-sm-8 col-md-10"><%= @address.smart_contract.evm_version %></dd>
</dl> </dl>
<% end %> <% end %>
<hr/>
<%= if @address.smart_contract.constructor_arguments do %> <%= if @address.smart_contract.constructor_arguments do %>
<dl class="row"> <section>
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Constructor arguments" %></dt> <div class="d-flex justify-content-between align-items-baseline">
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.constructor_arguments %></dd> <h3><%= gettext "Constructor Arguments" %></h3>
</dl>
<% end %>
</div> </div>
<hr/> <div class="tile tile-muted mb-4">
<pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= raw(format_constructor_arguments(@address.smart_contract)) %></code>
</pre>
</div>
</section>
<% end %>
<section> <section>
<div class="d-flex justify-content-between align-items-baseline"> <div class="d-flex justify-content-between align-items-baseline">
<h3><%= gettext "Contract source code" %></h3> <h3><%= gettext "Contract source code" %></h3>
@ -116,7 +120,7 @@
<h3><%= gettext "External libraries" %></h3> <h3><%= gettext "External libraries" %></h3>
</div> </div>
<div class="tile tile-muted mb-4"> <div class="tile tile-muted mb-4">
<pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= format_external_libraries(@address.smart_contract.external_libraries) %></code> <pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= raw(format_external_libraries(@address.smart_contract.external_libraries)) %></code>
</pre> </pre>
</div> </div>
</section> </section>

@ -5,7 +5,7 @@
<div class="dashboard-banner-network-graph"> <div class="dashboard-banner-network-graph">
<!-- Graph --> <!-- Graph -->
<div class="dashboard-banner-chart"> <div class="dashboard-banner-chart">
<div data-chart-loading-message class="tile tile-muted text-center mt-5"> <div hidden data-chart-loading-message class="tile tile-muted text-center mt-5">
<span class="loading-spinner-small mr-2"> <span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span> <span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span> <span class="loading-spinner-block-2"></span>
@ -91,7 +91,7 @@
<%= gettext "Something went wrong, click to reload." %> <%= gettext "Something went wrong, click to reload." %>
</span> </span>
</button> </button>
<div data-selector="loading-message" class="tile tile-muted text-center mt-3 w-100"> <div hidden data-selector="loading-message" class="tile tile-muted text-center mt-3 w-100" >
<span class="loading-spinner-small mr-2"> <span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span> <span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span> <span class="loading-spinner-block-2"></span>

@ -1,6 +1,7 @@
defmodule BlockScoutWeb.AddressContractView do defmodule BlockScoutWeb.AddressContractView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias ABI.{FunctionSelector, TypeDecoder}
alias Explorer.Chain.{Address, Data, InternalTransaction} alias Explorer.Chain.{Address, Data, InternalTransaction}
def render("scripts.html", %{conn: conn}) do def render("scripts.html", %{conn: conn}) do
@ -21,9 +22,44 @@ defmodule BlockScoutWeb.AddressContractView do
def format_optimization_text(true), do: gettext("true") def format_optimization_text(true), do: gettext("true")
def format_optimization_text(false), do: gettext("false") def format_optimization_text(false), do: gettext("false")
def format_constructor_arguments(contract) do
constructor_abi = Enum.find(contract.abi, fn el -> el["type"] == "constructor" && el["inputs"] != [] end)
input_types = Enum.map(constructor_abi["inputs"], &FunctionSelector.parse_specification_type/1)
{_, result} =
contract.constructor_arguments
|> decode_data(input_types)
|> Enum.zip(constructor_abi["inputs"])
|> Enum.reduce({0, "#{contract.constructor_arguments}\n\n"}, fn {val, %{"type" => type}}, {count, acc} ->
formatted_val =
if is_binary(val) do
Base.encode16(val, case: :lower)
else
val
end
{count + 1, "#{acc}Arg [#{count}] (<b>#{type}</b>) : #{formatted_val}\n"}
end)
result
rescue
_ -> contract.constructor_arguments
end
defp decode_data("0x" <> encoded_data, types) do
decode_data(encoded_data, types)
end
defp decode_data(encoded_data, types) do
encoded_data
|> Base.decode16!(case: :mixed)
|> TypeDecoder.decode_raw(types)
end
def format_external_libraries(libraries) do def format_external_libraries(libraries) do
Enum.reduce(libraries, "", fn %{name: name, address_hash: address_hash}, acc -> Enum.reduce(libraries, "", fn %{name: name, address_hash: address_hash}, acc ->
acc <> name <> " : " <> address_hash <> "\n" "#{acc}<span class=\"hljs-title\">#{name}</span> : #{address_hash} \n"
end) end)
end end

@ -258,7 +258,7 @@ msgid "Connection Lost, click to load newer validations"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:71 #: lib/block_scout_web/templates/address_contract/index.html.eex:75
msgid "Contract ABI" msgid "Contract ABI"
msgstr "" msgstr ""
@ -296,7 +296,7 @@ msgid "Contract name:"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:59 #: lib/block_scout_web/templates/address_contract/index.html.eex:63
msgid "Contract source code" msgid "Contract source code"
msgstr "" msgstr ""
@ -1001,7 +1001,7 @@ msgid "at"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/address_contract_view.ex:22 #: lib/block_scout_web/views/address_contract_view.ex:23
msgid "false" msgid "false"
msgstr "" msgstr ""
@ -1019,7 +1019,7 @@ msgid "string"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/address_contract_view.ex:21 #: lib/block_scout_web/views/address_contract_view.ex:22
msgid "true" msgid "true"
msgstr "" msgstr ""
@ -1401,17 +1401,17 @@ msgid "Support"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:73 #: lib/block_scout_web/templates/address_contract/index.html.eex:77
msgid "Copy ABI" msgid "Copy ABI"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:89 #: lib/block_scout_web/templates/address_contract/index.html.eex:93
msgid "Copy Contract Creation Code" msgid "Copy Contract Creation Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:61 #: lib/block_scout_web/templates/address_contract/index.html.eex:65
msgid "Copy Source Code" msgid "Copy Source Code"
msgstr "" msgstr ""
@ -1581,27 +1581,27 @@ msgid "Block Details"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:101 #: lib/block_scout_web/templates/address_contract/index.html.eex:105
msgid "Contract Byte Code" msgid "Contract Byte Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:87 #: lib/block_scout_web/templates/address_contract/index.html.eex:91
msgid "Contract Creation Code" msgid "Contract Creation Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:93 #: lib/block_scout_web/templates/address_contract/index.html.eex:97
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:103 #: lib/block_scout_web/templates/address_contract/index.html.eex:107
msgid "Copy Contract Byte Code" msgid "Copy Contract Byte Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:94 #: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Displaying the init data provided of the creating transaction." msgid "Displaying the init data provided of the creating transaction."
msgstr "" msgstr ""
@ -1749,11 +1749,6 @@ msgstr ""
msgid "Change Network" msgid "Change Network"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:51
msgid "Constructor arguments"
msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44 #: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 " msgid "ERC-20 "
@ -1765,7 +1760,7 @@ msgid "ERC-721 "
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:116 #: lib/block_scout_web/templates/address_contract/index.html.eex:120
msgid "External libraries" msgid "External libraries"
msgstr "" msgstr ""
@ -1798,3 +1793,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4 #: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4
msgid "Connection Lost" msgid "Connection Lost"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:53
msgid "Constructor Arguments"
msgstr ""

@ -258,7 +258,7 @@ msgid "Connection Lost, click to load newer validations"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:71 #: lib/block_scout_web/templates/address_contract/index.html.eex:75
msgid "Contract ABI" msgid "Contract ABI"
msgstr "" msgstr ""
@ -296,7 +296,7 @@ msgid "Contract name:"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:59 #: lib/block_scout_web/templates/address_contract/index.html.eex:63
msgid "Contract source code" msgid "Contract source code"
msgstr "" msgstr ""
@ -1001,7 +1001,7 @@ msgid "at"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/address_contract_view.ex:22 #: lib/block_scout_web/views/address_contract_view.ex:23
msgid "false" msgid "false"
msgstr "" msgstr ""
@ -1019,7 +1019,7 @@ msgid "string"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/address_contract_view.ex:21 #: lib/block_scout_web/views/address_contract_view.ex:22
msgid "true" msgid "true"
msgstr "" msgstr ""
@ -1401,17 +1401,17 @@ msgid "Support"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:73 #: lib/block_scout_web/templates/address_contract/index.html.eex:77
msgid "Copy ABI" msgid "Copy ABI"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:89 #: lib/block_scout_web/templates/address_contract/index.html.eex:93
msgid "Copy Contract Creation Code" msgid "Copy Contract Creation Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:61 #: lib/block_scout_web/templates/address_contract/index.html.eex:65
msgid "Copy Source Code" msgid "Copy Source Code"
msgstr "" msgstr ""
@ -1581,27 +1581,27 @@ msgid "Block Details"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:101 #: lib/block_scout_web/templates/address_contract/index.html.eex:105
msgid "Contract Byte Code" msgid "Contract Byte Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:87 #: lib/block_scout_web/templates/address_contract/index.html.eex:91
msgid "Contract Creation Code" msgid "Contract Creation Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:93 #: lib/block_scout_web/templates/address_contract/index.html.eex:97
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:103 #: lib/block_scout_web/templates/address_contract/index.html.eex:107
msgid "Copy Contract Byte Code" msgid "Copy Contract Byte Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:94 #: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Displaying the init data provided of the creating transaction." msgid "Displaying the init data provided of the creating transaction."
msgstr "" msgstr ""
@ -1749,11 +1749,6 @@ msgstr ""
msgid "Change Network" msgid "Change Network"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:51
msgid "Constructor arguments"
msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44 #: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 " msgid "ERC-20 "
@ -1765,7 +1760,7 @@ msgid "ERC-721 "
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:116 #: lib/block_scout_web/templates/address_contract/index.html.eex:120
msgid "External libraries" msgid "External libraries"
msgstr "" msgstr ""
@ -1798,3 +1793,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4 #: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4
msgid "Connection Lost" msgid "Connection Lost"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:53
msgid "Constructor Arguments"
msgstr ""

@ -17,7 +17,15 @@ config :explorer,
if(System.get_env("UNCLES_IN_AVERAGE_BLOCK_TIME") == "false", do: false, else: true), if(System.get_env("UNCLES_IN_AVERAGE_BLOCK_TIME") == "false", do: false, else: true),
healthy_blocks_period: System.get_env("HEALTHY_BLOCKS_PERIOD") || :timer.minutes(5) healthy_blocks_period: System.get_env("HEALTHY_BLOCKS_PERIOD") || :timer.minutes(5)
config :explorer, Explorer.Counters.AverageBlockTime, enabled: true average_block_period =
case Integer.parse(System.get_env("AVERAGE_BLOCK_CACHE_PERIOD", "")) do
{secs, ""} -> :timer.seconds(secs)
_ -> :timer.minutes(30)
end
config :explorer, Explorer.Counters.AverageBlockTime,
enabled: true,
period: average_block_period
config :explorer, Explorer.Chain.Cache.BlockNumber, enabled: true config :explorer, Explorer.Chain.Cache.BlockNumber, enabled: true
@ -106,6 +114,14 @@ config :spandex_ecto, SpandexEcto.EctoLogger,
tracer: Explorer.Tracer, tracer: Explorer.Tracer,
otp_app: :explorer otp_app: :explorer
market_history_cache_period =
case Integer.parse(System.get_env("MARKET_HISTORY_CACHE_PERIOD", "")) do
{secs, ""} -> :timer.seconds(secs)
_ -> :timer.hours(6)
end
config :explorer, Explorer.Market.MarketHistoryCache, period: market_history_cache_period
# 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"

@ -364,8 +364,8 @@ defmodule Explorer.Chain do
Uncles are not currently accounted for. Uncles are not currently accounted for.
""" """
@spec block_reward(Block.t()) :: Wei.t() @spec block_reward(Block.block_number()) :: Wei.t()
def block_reward(%Block{number: block_number}) do def block_reward(block_number) do
query = query =
from( from(
block in Block, block in Block,
@ -415,8 +415,8 @@ defmodule Explorer.Chain do
`:key` (a tuple of the lowest/oldest `{index}`) and. Results will be the transactions older than `:key` (a tuple of the lowest/oldest `{index}`) and. Results will be the transactions older than
the `index` that are passed. the `index` that are passed.
""" """
@spec block_to_transactions(Block.t(), [paging_options | necessity_by_association_option]) :: [Transaction.t()] @spec block_to_transactions(Hash.Full.t(), [paging_options | necessity_by_association_option]) :: [Transaction.t()]
def block_to_transactions(%Block{hash: block_hash}, options \\ []) when is_list(options) do def block_to_transactions(block_hash, options \\ []) when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
options options
@ -432,8 +432,8 @@ defmodule Explorer.Chain do
@doc """ @doc """
Counts the number of `t:Explorer.Chain.Transaction.t/0` in the `block`. Counts the number of `t:Explorer.Chain.Transaction.t/0` in the `block`.
""" """
@spec block_to_transaction_count(Block.t()) :: non_neg_integer() @spec block_to_transaction_count(Hash.Full.t()) :: non_neg_integer()
def block_to_transaction_count(%Block{hash: block_hash}) do def block_to_transaction_count(block_hash) do
query = query =
from( from(
transaction in Transaction, transaction in Transaction,
@ -843,7 +843,7 @@ defmodule Explorer.Chain do
Returns `{:error, :not_found}` if there is no address by that hash present. Returns `{:error, :not_found}` if there is no address by that hash present.
Returns `{:error, :no_balance}` if there is no balance for that address at that block. Returns `{:error, :no_balance}` if there is no balance for that address at that block.
""" """
@spec get_balance_as_of_block(Hash.Address.t(), integer | :earliest | :latest | :pending) :: @spec get_balance_as_of_block(Hash.Address.t(), Block.block_number() | :earliest | :latest | :pending) ::
{:ok, Wei.t()} | {:error, :no_balance} | {:error, :not_found} {:ok, Wei.t()} | {:error, :no_balance} | {:error, :not_found}
def get_balance_as_of_block(address, block) when is_integer(block) do def get_balance_as_of_block(address, block) when is_integer(block) do
coin_balance_query = coin_balance_query =
@ -930,33 +930,44 @@ defmodule Explorer.Chain do
Repo.all(query) Repo.all(query)
end end
@spec find_contract_address(Hash.t()) :: {:ok, Address.t()} | {:error, :not_found} @doc """
def find_contract_address(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do Finds an `t:Explorer.Chain.Address.t/0` that has the provided `t:Explorer.Chain.Address.t/0` `hash` and a contract.
## Options
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`:required`, and the `t:Explorer.Chain.Address.t/0` has no associated record for that association,
then the `t:Explorer.Chain.Address.t/0` will not be included in the list.
Optionally it also accepts a boolean to fetch the `has_decompiled_code?` virtual field or not
"""
@spec find_contract_address(Hash.Address.t(), [necessity_by_association_option], boolean()) ::
{:ok, Address.t()} | {:error, :not_found}
def find_contract_address(
%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash,
options \\ [],
query_decompiled_code_flag \\ false
) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
query = query =
from( from(
address in Address, address in Address,
preload: [
:contracts_creation_internal_transaction,
:names,
:smart_contract,
:token,
:contracts_creation_transaction
],
where: address.hash == ^hash and not is_nil(address.contract_code) where: address.hash == ^hash and not is_nil(address.contract_code)
) )
query_with_decompiled_flag = with_decompiled_code_flag(query, hash) query
|> join_associations(necessity_by_association)
address = Repo.one(query_with_decompiled_flag) |> with_decompiled_code_flag(hash, query_decompiled_code_flag)
|> Repo.one()
if address do |> case do
{:ok, address} nil -> {:error, :not_found}
else address -> {:ok, address}
{:error, :not_found}
end end
end end
@spec find_decompiled_contract_address(Hash.t()) :: {:ok, Address.t()} | {:error, :not_found} @spec find_decompiled_contract_address(Hash.Address.t()) :: {:ok, Address.t()} | {:error, :not_found}
def find_decompiled_contract_address(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do def find_decompiled_contract_address(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do
query = query =
from( from(
@ -2209,14 +2220,10 @@ defmodule Explorer.Chain do
""" """
@spec transaction_to_internal_transactions(Transaction.t(), [paging_options | necessity_by_association_option]) :: [ @spec transaction_to_internal_transactions(Hash.Full.t(), [paging_options | necessity_by_association_option]) :: [
InternalTransaction.t() InternalTransaction.t()
] ]
def transaction_to_internal_transactions( def transaction_to_internal_transactions(hash, options \\ []) when is_list(options) do
%Transaction{hash: %Hash{byte_count: unquote(Hash.Full.byte_count())} = hash},
options \\ []
)
when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options) paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@ -2244,12 +2251,8 @@ defmodule Explorer.Chain do
the `index` that are passed. the `index` that are passed.
""" """
@spec transaction_to_logs(Transaction.t(), [paging_options | necessity_by_association_option]) :: [Log.t()] @spec transaction_to_logs(Hash.Full.t(), [paging_options | necessity_by_association_option]) :: [Log.t()]
def transaction_to_logs( def transaction_to_logs(transaction_hash, options \\ []) when is_list(options) do
%Transaction{hash: %Hash{byte_count: unquote(Hash.Full.byte_count())} = transaction_hash},
options \\ []
)
when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options) paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@ -2276,14 +2279,10 @@ defmodule Explorer.Chain do
the `index` that are passed. the `index` that are passed.
""" """
@spec transaction_to_token_transfers(Transaction.t(), [paging_options | necessity_by_association_option]) :: [ @spec transaction_to_token_transfers(Hash.Full.t(), [paging_options | necessity_by_association_option]) :: [
TokenTransfer.t() TokenTransfer.t()
] ]
def transaction_to_token_transfers( def transaction_to_token_transfers(transaction_hash, options \\ []) when is_list(options) do
%Transaction{hash: %Hash{byte_count: unquote(Hash.Full.byte_count())} = transaction_hash},
options \\ []
)
when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options) paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@ -2510,16 +2509,16 @@ defmodule Explorer.Chain do
|> repo.insert(on_conflict: :nothing, conflict_target: [:address_hash, :name]) |> repo.insert(on_conflict: :nothing, conflict_target: [:address_hash, :name])
end end
@spec address_hash_to_address_with_source_code(%Explorer.Chain.Hash{}) :: %Explorer.Chain.Address{} | nil @spec address_hash_to_address_with_source_code(Hash.Address.t()) :: Address.t() | nil
def address_hash_to_address_with_source_code(%Explorer.Chain.Hash{} = address_hash) do def address_hash_to_address_with_source_code(address_hash) do
case Repo.get(Address, address_hash) do case Repo.get(Address, address_hash) do
nil -> nil nil -> nil
address -> Repo.preload(address, [:smart_contract, :decompiled_smart_contracts]) address -> Repo.preload(address, [:smart_contract, :decompiled_smart_contracts])
end end
end end
@spec address_hash_to_smart_contract(%Explorer.Chain.Hash{}) :: %Explorer.Chain.SmartContract{} | nil @spec address_hash_to_smart_contract(Hash.Address.t()) :: SmartContract.t() | nil
def address_hash_to_smart_contract(%Explorer.Chain.Hash{} = address_hash) do def address_hash_to_smart_contract(address_hash) do
query = query =
from( from(
smart_contract in SmartContract, smart_contract in SmartContract,
@ -3276,8 +3275,6 @@ defmodule Explorer.Chain do
defp staking_pool_filter(query, _), do: query defp staking_pool_filter(query, _), do: query
defp with_decompiled_code_flag(query, hash, use_option \\ true)
defp with_decompiled_code_flag(query, _hash, false), do: query defp with_decompiled_code_flag(query, _hash, false), do: query
defp with_decompiled_code_flag(query, hash, true) do defp with_decompiled_code_flag(query, hash, true) do
@ -3300,4 +3297,203 @@ defmodule Explorer.Chain do
|> Base.decode16!(case: :mixed) |> Base.decode16!(case: :mixed)
|> TypeDecoder.decode_raw(types) |> TypeDecoder.decode_raw(types)
end end
@doc """
Checks if an `t:Explorer.Chain.Address.t/0` with the given `hash` exists.
Returns `:ok` if found
iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.create_address(
...> %{hash: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"}
...> )
iex> Explorer.Chain.check_address_exists(hash)
:ok
Returns `:not_found` if not found
iex> {:ok, hash} = Explorer.Chain.string_to_address_hash("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
iex> Explorer.Chain.check_address_exists(hash)
:not_found
"""
@spec check_address_exists(Hash.Address.t()) :: :ok | :not_found
def check_address_exists(address_hash) do
address_hash
|> address_exists?()
|> boolean_to_check_result()
end
@doc """
Checks if an `t:Explorer.Chain.Address.t/0` with the given `hash` exists.
Returns `true` if found
iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.create_address(
...> %{hash: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"}
...> )
iex> Explorer.Chain.address_exists?(hash)
true
Returns `false` if not found
iex> {:ok, hash} = Explorer.Chain.string_to_address_hash("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
iex> Explorer.Chain.address_exists?(hash)
false
"""
@spec address_exists?(Hash.Address.t()) :: boolean()
def address_exists?(address_hash) do
query =
from(
address in Address,
where: address.hash == ^address_hash
)
Repo.exists?(query)
end
@doc """
Checks if it exists an `t:Explorer.Chain.Address.t/0` that has the provided
`t:Explorer.Chain.Address.t/0` `hash` and a contract.
Returns `:ok` if found and `:not_found` otherwise.
"""
@spec check_contract_address_exists(Hash.Address.t()) :: :ok | :not_found
def check_contract_address_exists(address_hash) do
address_hash
|> contract_address_exists?()
|> boolean_to_check_result()
end
@doc """
Checks if it exists an `t:Explorer.Chain.Address.t/0` that has the provided
`t:Explorer.Chain.Address.t/0` `hash` and a contract.
Returns `true` if found and `false` otherwise.
"""
@spec contract_address_exists?(Hash.Address.t()) :: boolean()
def contract_address_exists?(address_hash) do
query =
from(
address in Address,
where: address.hash == ^address_hash and not is_nil(address.contract_code)
)
Repo.exists?(query)
end
@doc """
Checks if it exists a `t:Explorer.Chain.DecompiledSmartContract.t/0` for the
`t:Explorer.Chain.Address.t/0` with the provided `hash` and with the provided version.
Returns `:ok` if found and `:not_found` otherwise.
"""
@spec check_decompiled_contract_exists(Hash.Address.t(), String.t()) :: :ok | :not_found
def check_decompiled_contract_exists(address_hash, version) do
address_hash
|> decompiled_contract_exists?(version)
|> boolean_to_check_result()
end
@doc """
Checks if it exists a `t:Explorer.Chain.DecompiledSmartContract.t/0` for the
`t:Explorer.Chain.Address.t/0` with the provided `hash` and with the provided version.
Returns `true` if found and `false` otherwise.
"""
@spec decompiled_contract_exists?(Hash.Address.t(), String.t()) :: boolean()
def decompiled_contract_exists?(address_hash, version) do
query =
from(contract in DecompiledSmartContract,
where: contract.address_hash == ^address_hash and contract.decompiler_version == ^version
)
Repo.exists?(query)
end
@doc """
Checks if it exists a verified `t:Explorer.Chain.SmartContract.t/0` for the
`t:Explorer.Chain.Address.t/0` with the provided `hash`.
Returns `:ok` if found and `:not_found` otherwise.
"""
@spec check_verified_smart_contract_exists(Hash.Address.t()) :: :ok | :not_found
def check_verified_smart_contract_exists(address_hash) do
address_hash
|> verified_smart_contract_exists?()
|> boolean_to_check_result()
end
@doc """
Checks if it exists a verified `t:Explorer.Chain.SmartContract.t/0` for the
`t:Explorer.Chain.Address.t/0` with the provided `hash`.
Returns `true` if found and `false` otherwise.
"""
@spec verified_smart_contract_exists?(Hash.Address.t()) :: boolean()
def verified_smart_contract_exists?(address_hash) do
query =
from(
smart_contract in SmartContract,
where: smart_contract.address_hash == ^address_hash
)
Repo.exists?(query)
end
@doc """
Checks if a `t:Explorer.Chain.Transaction.t/0` with the given `hash` exists.
Returns `:ok` if found
iex> %Transaction{hash: hash} = insert(:transaction)
iex> Explorer.Chain.check_transaction_exists(hash)
:ok
Returns `:not_found` if not found
iex> {:ok, hash} = Explorer.Chain.string_to_transaction_hash(
...> "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b"
...> )
iex> Explorer.Chain.check_transaction_exists(hash)
:not_found
"""
@spec check_transaction_exists(Hash.Full.t()) :: :ok | :not_found
def check_transaction_exists(hash) do
hash
|> transaction_exists?()
|> boolean_to_check_result()
end
@doc """
Checks if a `t:Explorer.Chain.Transaction.t/0` with the given `hash` exists.
Returns `true` if found
iex> %Transaction{hash: hash} = insert(:transaction)
iex> Explorer.Chain.transaction_exists?(hash)
true
Returns `false` if not found
iex> {:ok, hash} = Explorer.Chain.string_to_transaction_hash(
...> "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b"
...> )
iex> Explorer.Chain.transaction_exists?(hash)
false
"""
@spec transaction_exists?(Hash.Full.t()) :: boolean()
def transaction_exists?(hash) do
query =
from(
transaction in Transaction,
where: transaction.hash == ^hash
)
Repo.exists?(query)
end
defp boolean_to_check_result(true), do: :ok
defp boolean_to_check_result(false), do: :not_found
end end

@ -11,7 +11,7 @@ defmodule Explorer.Counters.AverageBlockTime do
alias Explorer.Repo alias Explorer.Repo
alias Timex.Duration alias Timex.Duration
@refresh_period 30 * 60 * 1_000 @refresh_period Application.get_env(:explorer, __MODULE__)[:period]
@doc """ @doc """
Starts a process to periodically update the counter of the token holders. Starts a process to periodically update the counter of the token holders.

@ -12,7 +12,7 @@ defmodule Explorer.Market.MarketHistoryCache do
@last_update_key :last_update @last_update_key :last_update
@history_key :history @history_key :history
# 6 hours # 6 hours
@cache_period 1_000 * 60 * 60 * 6 @cache_period Application.get_env(:explorer, __MODULE__)[:period]
@recent_days 30 @recent_days 30
def fetch do def fetch do

@ -8,7 +8,7 @@ defmodule Explorer.SmartContract.Reader do
alias EthereumJSONRPC.Contract alias EthereumJSONRPC.Contract
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Hash alias Explorer.Chain.{Hash, SmartContract}
@typedoc """ @typedoc """
Map of functions to call with the values for the function to be called with. Map of functions to call with the values for the function to be called with.
@ -34,6 +34,8 @@ defmodule Explorer.SmartContract.Reader do
@doc """ @doc """
Queries the contract functions on the blockchain and returns the call results. Queries the contract functions on the blockchain and returns the call results.
Optionally accepts the abi if it has already been fetched.
## Examples ## Examples
Note that for this example to work the database must be up to date with the Note that for this example to work the database must be up to date with the
@ -57,15 +59,21 @@ defmodule Explorer.SmartContract.Reader do
) )
# => %{"sum" => {:error, "Data overflow encoding int, data `abc` cannot fit in 256 bits"}} # => %{"sum" => {:error, "Data overflow encoding int, data `abc` cannot fit in 256 bits"}}
""" """
@spec query_verified_contract(Hash.Address.t(), functions()) :: functions_results() @spec query_verified_contract(Hash.Address.t(), functions(), SmartContract.abi() | nil) :: functions_results()
def query_verified_contract(address_hash, functions) do def query_verified_contract(address_hash, functions, mabi \\ nil) do
contract_address = Hash.to_string(address_hash) contract_address = Hash.to_string(address_hash)
abi = abi =
case mabi do
nil ->
address_hash address_hash
|> Chain.address_hash_to_smart_contract() |> Chain.address_hash_to_smart_contract()
|> Map.get(:abi) |> Map.get(:abi)
_ ->
mabi
end
query_contract(contract_address, abi, functions) query_contract(contract_address, abi, functions)
end end
@ -156,41 +164,41 @@ defmodule Explorer.SmartContract.Reader do
""" """
@spec read_only_functions(Hash.t()) :: [%{}] @spec read_only_functions(Hash.t()) :: [%{}]
def read_only_functions(contract_address_hash) do def read_only_functions(contract_address_hash) do
abi =
contract_address_hash contract_address_hash
|> Chain.address_hash_to_smart_contract() |> Chain.address_hash_to_smart_contract()
|> Map.get(:abi, []) |> Map.get(:abi)
case abi do
nil ->
[]
_ ->
abi
|> Enum.filter(& &1["constant"]) |> Enum.filter(& &1["constant"])
|> fetch_current_value_from_blockchain(contract_address_hash, []) |> Enum.map(&fetch_current_value_from_blockchain(&1, abi, contract_address_hash))
|> Enum.reverse() end
end end
def fetch_current_value_from_blockchain( defp fetch_current_value_from_blockchain(function, abi, contract_address_hash) do
[%{"inputs" => []} = function | tail],
contract_address_hash,
acc
) do
values = values =
fetch_from_blockchain(contract_address_hash, %{ case function do
name: function["name"], %{"inputs" => []} ->
args: function["inputs"], name = function["name"]
outputs: function["outputs"] args = function["inputs"]
}) outputs = function["outputs"]
formatted = Map.replace!(function, "outputs", values) contract_address_hash
|> query_verified_contract(%{name => normalize_args(args)}, abi)
|> link_outputs_and_values(outputs, name)
fetch_current_value_from_blockchain(tail, contract_address_hash, [formatted | acc]) _ ->
link_outputs_and_values(%{}, Map.get(function, "outputs", []), function["name"])
end end
def fetch_current_value_from_blockchain([function | tail], contract_address_hash, acc) do Map.replace!(function, "outputs", values)
values = link_outputs_and_values(%{}, Map.get(function, "outputs", []), function["name"])
formatted = Map.replace!(function, "outputs", values)
fetch_current_value_from_blockchain(tail, contract_address_hash, [formatted | acc])
end end
def fetch_current_value_from_blockchain([], _contract_address_hash, acc), do: acc
@doc """ @doc """
Fetches the blockchain value of a function that requires arguments. Fetches the blockchain value of a function that requires arguments.
""" """
@ -201,23 +209,27 @@ defmodule Explorer.SmartContract.Reader do
@spec query_function(Hash.t(), %{name: String.t(), args: [term()]}) :: [%{}] @spec query_function(Hash.t(), %{name: String.t(), args: [term()]}) :: [%{}]
def query_function(contract_address_hash, %{name: name, args: args}) do def query_function(contract_address_hash, %{name: name, args: args}) do
function = abi =
contract_address_hash contract_address_hash
|> Chain.address_hash_to_smart_contract() |> Chain.address_hash_to_smart_contract()
|> Map.get(:abi, []) |> Map.get(:abi)
outputs =
case abi do
nil ->
nil
_ ->
function =
abi
|> Enum.filter(fn function -> function["name"] == name end) |> Enum.filter(fn function -> function["name"] == name end)
|> List.first() |> List.first()
fetch_from_blockchain(contract_address_hash, %{ function["outputs"]
name: name,
args: args,
outputs: function["outputs"]
})
end end
defp fetch_from_blockchain(contract_address_hash, %{name: name, args: args, outputs: outputs}) do
contract_address_hash contract_address_hash
|> query_verified_contract(%{name => normalize_args(args)}) |> query_verified_contract(%{name => normalize_args(args)}, abi)
|> link_outputs_and_values(outputs, name) |> link_outputs_and_values(outputs, name)
end end

@ -630,7 +630,7 @@ defmodule Explorer.ChainTest do
assert Repo.aggregate(Transaction, :count, :hash) == 0 assert Repo.aggregate(Transaction, :count, :hash) == 0
assert [] = Chain.block_to_transactions(block) assert [] = Chain.block_to_transactions(block.hash)
end end
test "with transactions" do test "with transactions" do
@ -639,7 +639,7 @@ defmodule Explorer.ChainTest do
|> insert() |> insert()
|> with_block() |> with_block()
assert [%Transaction{hash: ^transaction_hash}] = Chain.block_to_transactions(block) assert [%Transaction{hash: ^transaction_hash}] = Chain.block_to_transactions(block.hash)
end end
test "with transactions can be paginated by {index}" do test "with transactions can be paginated by {index}" do
@ -657,7 +657,7 @@ defmodule Explorer.ChainTest do
|> with_block(block) |> with_block(block)
assert second_page_hashes == assert second_page_hashes ==
block block.hash
|> Chain.block_to_transactions(paging_options: %PagingOptions{key: {index}, page_size: 50}) |> Chain.block_to_transactions(paging_options: %PagingOptions{key: {index}, page_size: 50})
|> Enum.map(& &1.hash) |> Enum.map(& &1.hash)
|> Enum.reverse() |> Enum.reverse()
@ -683,7 +683,7 @@ defmodule Explorer.ChainTest do
token: token token: token
) )
fetched_transaction = List.first(Explorer.Chain.block_to_transactions(block)) fetched_transaction = List.first(Explorer.Chain.block_to_transactions(block.hash))
assert fetched_transaction.hash == transaction.hash assert fetched_transaction.hash == transaction.hash
assert length(fetched_transaction.token_transfers) == 2 assert length(fetched_transaction.token_transfers) == 2
end end
@ -693,7 +693,7 @@ defmodule Explorer.ChainTest do
test "without transactions" do test "without transactions" do
block = insert(:block) block = insert(:block)
assert Chain.block_to_transaction_count(block) == 0 assert Chain.block_to_transaction_count(block.hash) == 0
end end
test "with transactions" do test "with transactions" do
@ -702,7 +702,7 @@ defmodule Explorer.ChainTest do
|> insert() |> insert()
|> with_block() |> with_block()
assert Chain.block_to_transaction_count(block) == 1 assert Chain.block_to_transaction_count(block.hash) == 1
end end
end end
@ -2090,7 +2090,7 @@ defmodule Explorer.ChainTest do
test "with transaction without internal transactions" do test "with transaction without internal transactions" do
transaction = insert(:transaction) transaction = insert(:transaction)
assert [] = Chain.transaction_to_internal_transactions(transaction) assert [] = Chain.transaction_to_internal_transactions(transaction.hash)
end end
test "with transaction with internal transactions returns all internal transactions for a given transaction hash" do test "with transaction with internal transactions returns all internal transactions for a given transaction hash" do
@ -2117,7 +2117,7 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index transaction_index: transaction.index
) )
results = [internal_transaction | _] = Chain.transaction_to_internal_transactions(transaction) results = [internal_transaction | _] = Chain.transaction_to_internal_transactions(transaction.hash)
assert 2 == length(results) assert 2 == length(results)
@ -2151,7 +2151,7 @@ defmodule Explorer.ChainTest do
to_address: %Ecto.Association.NotLoaded{}, to_address: %Ecto.Association.NotLoaded{},
transaction: %Transaction{block: %Ecto.Association.NotLoaded{}} transaction: %Transaction{block: %Ecto.Association.NotLoaded{}}
} }
] = Chain.transaction_to_internal_transactions(transaction) ] = Chain.transaction_to_internal_transactions(transaction.hash)
assert [ assert [
%InternalTransaction{ %InternalTransaction{
@ -2161,7 +2161,7 @@ defmodule Explorer.ChainTest do
} }
] = ] =
Chain.transaction_to_internal_transactions( Chain.transaction_to_internal_transactions(
transaction, transaction.hash,
necessity_by_association: %{ necessity_by_association: %{
:from_address => :optional, :from_address => :optional,
:to_address => :optional, :to_address => :optional,
@ -2183,7 +2183,7 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index transaction_index: transaction.index
) )
result = Chain.transaction_to_internal_transactions(transaction) result = Chain.transaction_to_internal_transactions(transaction.hash)
assert Enum.empty?(result) assert Enum.empty?(result)
end end
@ -2202,7 +2202,7 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index transaction_index: transaction.index
) )
actual = Enum.at(Chain.transaction_to_internal_transactions(transaction), 0) actual = Enum.at(Chain.transaction_to_internal_transactions(transaction.hash), 0)
assert {actual.transaction_hash, actual.index} == {expected.transaction_hash, expected.index} assert {actual.transaction_hash, actual.index} == {expected.transaction_hash, expected.index}
end end
@ -2222,7 +2222,7 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index transaction_index: transaction.index
) )
actual = Enum.at(Chain.transaction_to_internal_transactions(transaction), 0) actual = Enum.at(Chain.transaction_to_internal_transactions(transaction.hash), 0)
assert {actual.transaction_hash, actual.index} == {expected.transaction_hash, expected.index} assert {actual.transaction_hash, actual.index} == {expected.transaction_hash, expected.index}
end end
@ -2243,7 +2243,7 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index transaction_index: transaction.index
) )
actual = Enum.at(Chain.transaction_to_internal_transactions(transaction), 0) actual = Enum.at(Chain.transaction_to_internal_transactions(transaction.hash), 0)
assert {actual.transaction_hash, actual.index} == {expected.transaction_hash, expected.index} assert {actual.transaction_hash, actual.index} == {expected.transaction_hash, expected.index}
end end
@ -2271,7 +2271,7 @@ defmodule Explorer.ChainTest do
) )
result = result =
transaction transaction.hash
|> Chain.transaction_to_internal_transactions() |> Chain.transaction_to_internal_transactions()
|> Enum.map(&{&1.transaction_hash, &1.index}) |> Enum.map(&{&1.transaction_hash, &1.index})
@ -2301,17 +2301,17 @@ defmodule Explorer.ChainTest do
) )
assert [{first_transaction_hash, first_index}, {second_transaction_hash, second_index}] == assert [{first_transaction_hash, first_index}, {second_transaction_hash, second_index}] ==
transaction transaction.hash
|> Chain.transaction_to_internal_transactions(paging_options: %PagingOptions{key: {-1}, page_size: 2}) |> Chain.transaction_to_internal_transactions(paging_options: %PagingOptions{key: {-1}, page_size: 2})
|> Enum.map(&{&1.transaction_hash, &1.index}) |> Enum.map(&{&1.transaction_hash, &1.index})
assert [{first_transaction_hash, first_index}] == assert [{first_transaction_hash, first_index}] ==
transaction transaction.hash
|> Chain.transaction_to_internal_transactions(paging_options: %PagingOptions{key: {-1}, page_size: 1}) |> Chain.transaction_to_internal_transactions(paging_options: %PagingOptions{key: {-1}, page_size: 1})
|> Enum.map(&{&1.transaction_hash, &1.index}) |> Enum.map(&{&1.transaction_hash, &1.index})
assert [{second_transaction_hash, second_index}] == assert [{second_transaction_hash, second_index}] ==
transaction transaction.hash
|> Chain.transaction_to_internal_transactions(paging_options: %PagingOptions{key: {0}, page_size: 2}) |> Chain.transaction_to_internal_transactions(paging_options: %PagingOptions{key: {0}, page_size: 2})
|> Enum.map(&{&1.transaction_hash, &1.index}) |> Enum.map(&{&1.transaction_hash, &1.index})
end end
@ -2321,7 +2321,7 @@ defmodule Explorer.ChainTest do
test "without logs" do test "without logs" do
transaction = insert(:transaction) transaction = insert(:transaction)
assert [] = Chain.transaction_to_logs(transaction) assert [] = Chain.transaction_to_logs(transaction.hash)
end end
test "with logs" do test "with logs" do
@ -2332,7 +2332,7 @@ defmodule Explorer.ChainTest do
%Log{transaction_hash: transaction_hash, index: index} = insert(:log, transaction: transaction) %Log{transaction_hash: transaction_hash, index: index} = insert(:log, transaction: transaction)
assert [%Log{transaction_hash: ^transaction_hash, index: ^index}] = Chain.transaction_to_logs(transaction) assert [%Log{transaction_hash: ^transaction_hash, index: ^index}] = Chain.transaction_to_logs(transaction.hash)
end end
test "with logs can be paginated" do test "with logs can be paginated" do
@ -2349,7 +2349,7 @@ defmodule Explorer.ChainTest do
|> Enum.map(& &1.index) |> Enum.map(& &1.index)
assert second_page_indexes == assert second_page_indexes ==
transaction transaction.hash
|> Chain.transaction_to_logs(paging_options: %PagingOptions{key: {log.index}, page_size: 50}) |> Chain.transaction_to_logs(paging_options: %PagingOptions{key: {log.index}, page_size: 50})
|> Enum.map(& &1.index) |> Enum.map(& &1.index)
end end
@ -2364,7 +2364,7 @@ defmodule Explorer.ChainTest do
assert [%Log{address: %Address{}, transaction: %Transaction{}}] = assert [%Log{address: %Address{}, transaction: %Transaction{}}] =
Chain.transaction_to_logs( Chain.transaction_to_logs(
transaction, transaction.hash,
necessity_by_association: %{ necessity_by_association: %{
address: :optional, address: :optional,
transaction: :optional transaction: :optional
@ -2376,7 +2376,7 @@ defmodule Explorer.ChainTest do
address: %Ecto.Association.NotLoaded{}, address: %Ecto.Association.NotLoaded{},
transaction: %Ecto.Association.NotLoaded{} transaction: %Ecto.Association.NotLoaded{}
} }
] = Chain.transaction_to_logs(transaction) ] = Chain.transaction_to_logs(transaction.hash)
end end
end end
@ -2384,7 +2384,7 @@ defmodule Explorer.ChainTest do
test "without token transfers" do test "without token transfers" do
transaction = insert(:transaction) transaction = insert(:transaction)
assert [] = Chain.transaction_to_token_transfers(transaction) assert [] = Chain.transaction_to_token_transfers(transaction.hash)
end end
test "with token transfers" do test "with token transfers" do
@ -2397,7 +2397,7 @@ defmodule Explorer.ChainTest do
insert(:token_transfer, transaction: transaction) insert(:token_transfer, transaction: transaction)
assert [%TokenTransfer{transaction_hash: ^transaction_hash, log_index: ^log_index}] = assert [%TokenTransfer{transaction_hash: ^transaction_hash, log_index: ^log_index}] =
Chain.transaction_to_token_transfers(transaction) Chain.transaction_to_token_transfers(transaction.hash)
end end
test "token transfers necessity_by_association loads associations" do test "token transfers necessity_by_association loads associations" do
@ -2410,7 +2410,7 @@ defmodule Explorer.ChainTest do
assert [%TokenTransfer{token: %Token{}, transaction: %Transaction{}}] = assert [%TokenTransfer{token: %Token{}, transaction: %Transaction{}}] =
Chain.transaction_to_token_transfers( Chain.transaction_to_token_transfers(
transaction, transaction.hash,
necessity_by_association: %{ necessity_by_association: %{
token: :optional, token: :optional,
transaction: :optional transaction: :optional
@ -2422,7 +2422,7 @@ defmodule Explorer.ChainTest do
token: %Ecto.Association.NotLoaded{}, token: %Ecto.Association.NotLoaded{},
transaction: %Ecto.Association.NotLoaded{} transaction: %Ecto.Association.NotLoaded{}
} }
] = Chain.transaction_to_token_transfers(transaction) ] = Chain.transaction_to_token_transfers(transaction.hash)
end end
end end
@ -2480,7 +2480,17 @@ defmodule Explorer.ChainTest do
insert(:address, contract_code: Factory.data("contract_code"), smart_contract: nil, names: []) insert(:address, contract_code: Factory.data("contract_code"), smart_contract: nil, names: [])
|> Repo.preload([:contracts_creation_internal_transaction, :contracts_creation_transaction, :token]) |> Repo.preload([:contracts_creation_internal_transaction, :contracts_creation_transaction, :token])
response = Chain.find_contract_address(address.hash) options = [
necessity_by_association: %{
:contracts_creation_internal_transaction => :optional,
:names => :optional,
:smart_contract => :optional,
:token => :optional,
:contracts_creation_transaction => :optional
}
]
response = Chain.find_contract_address(address.hash, options, true)
assert response == {:ok, address} assert response == {:ok, address}
end end
@ -2523,11 +2533,11 @@ defmodule Explorer.ChainTest do
|> Decimal.add(Decimal.new(3)) |> Decimal.add(Decimal.new(3))
|> Wei.from(:wei) |> Wei.from(:wei)
assert expected == Chain.block_reward(block) assert expected == Chain.block_reward(block.number)
end end
test "with block without transactions", %{block: block, emission_reward: emission_reward} do test "with block without transactions", %{block: block, emission_reward: emission_reward} do
assert emission_reward.reward == Chain.block_reward(block) assert emission_reward.reward == Chain.block_reward(block.number)
end end
end end

@ -102,7 +102,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end end
end end
describe "query_verified_contract/2" do describe "query_verified_contract/3" do
test "correctly returns the results of the smart contract functions" do test "correctly returns the results of the smart contract functions" do
hash = hash =
:smart_contract :smart_contract

Loading…
Cancel
Save