Merge with master

pull/2412/head
Victor Baranov 5 years ago
commit e53c1469f7
  1. 5
      CHANGELOG.md
  2. 37
      apps/block_scout_web/assets/css/components/_stakes_table.scss
  3. 46
      apps/block_scout_web/assets/css/components/_tile.scss
  4. 6
      apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex
  5. 12
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex
  6. 10
      apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex
  7. 12
      apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex
  8. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_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. 14
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  15. 6
      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. 2
      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/router.ex
  22. 2
      apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex
  23. 2
      apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex
  24. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex
  25. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex
  26. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex
  27. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex
  28. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex
  29. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex
  30. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex
  31. 4
      apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex
  32. 4
      apps/block_scout_web/lib/block_scout_web/templates/block_transaction/index.html.eex
  33. 8
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  34. 85
      apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex
  35. 72
      apps/block_scout_web/lib/block_scout_web/templates/common_components/_tile-loader.html.eex
  36. 9
      apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex
  37. 4
      apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex
  38. 4
      apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/index.html.eex
  39. 4
      apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex
  40. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex
  41. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex
  42. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex
  43. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/index.html.eex
  44. 7
      apps/block_scout_web/priv/gettext/default.pot
  45. 7
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  46. 4
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex
  47. 10
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/transaction_test.exs
  48. 294
      apps/explorer/lib/explorer/chain.ex
  49. 104
      apps/explorer/lib/explorer/smart_contract/reader.ex
  50. 70
      apps/explorer/test/explorer/chain_test.exs
  51. 2
      apps/explorer/test/explorer/smart_contract/reader_test.exs

@ -2,6 +2,7 @@
### Features ### Features
- [#2412](https://github.com/poanetwork/blockscout/pull/2412) - dark theme - [#2412](https://github.com/poanetwork/blockscout/pull/2412) - dark theme
- [#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 - [#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 - [#2366](https://github.com/poanetwork/blockscout/pull/2366) - paginate eth logs
@ -11,10 +12,13 @@
- [#2324](https://github.com/poanetwork/blockscout/pull/2324) - set timeout for loading message on the main page - [#2324](https://github.com/poanetwork/blockscout/pull/2324) - set timeout for loading message on the main page
### Fixes ### Fixes
- [#2416](https://github.com/poanetwork/blockscout/pull/2416) - Fix "page not found" handling in the router
- [#2410](https://github.com/poanetwork/blockscout/pull/2410) - preload smart contract for logs decoding - [#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
- [#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
- [#2389](https://github.com/poanetwork/blockscout/pull/2389) - Reduce Lodash lib size (86% of lib methods are not used) - [#2389](https://github.com/poanetwork/blockscout/pull/2389) - Reduce Lodash lib size (86% of lib methods are not used)
- [#2387](https://github.com/poanetwork/blockscout/pull/2387) - fix not existing keys in transaction json rpc
- [#2378](https://github.com/poanetwork/blockscout/pull/2378) - Page performance: exclude moment.js localization files except EN, remove unused css - [#2378](https://github.com/poanetwork/blockscout/pull/2378) - Page performance: exclude moment.js localization files except EN, remove unused css
- [#2368](https://github.com/poanetwork/blockscout/pull/2368) - add two columns of smart contract info - [#2368](https://github.com/poanetwork/blockscout/pull/2368) - add two columns of smart contract info
- [#2375](https://github.com/poanetwork/blockscout/pull/2375) - Update created_contract_code_indexed_at on transaction import conflict - [#2375](https://github.com/poanetwork/blockscout/pull/2375) - Update created_contract_code_indexed_at on transaction import conflict
@ -32,6 +36,7 @@
- [#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 - [#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

@ -26,6 +26,43 @@ $stakes-table-cell-separation: 25px !default;
} }
} }
// Loader
.table-content-loader {
display: inline-block;
height: 24px;
width: 100%;
border-radius: 4px;
background-color: #f5f6fa;
overflow: hidden;
position: relative;
&:before {
width: inherit;
height: inherit;
content: '';
position: absolute;
background: linear-gradient(to right, #f5f6fa 2%, #eee 18%, #f5f6fa 33%);
animation-duration: 1.7s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-name: placeholderAnimate;
background-size: 1300px;
}
}
@keyframes placeholderAnimate {
0%{ background-position: -650px 0; }
100% { background-position: 650px 0; }
}
.table-content-pseudo {
td {
&:last-child {
padding-right: 24px !important;
}
}
}
.stakes-table { .stakes-table {
min-width: fit-content; min-width: fit-content;
width: 100%; width: 100%;

@ -349,6 +349,52 @@ $tile-body-a-color: #5959d8 !default;
} }
} }
// Loader
.tile-type-loading {
background-color: #fff;
padding-top: 30px;
padding-bottom: 28px;
}
.tile-loader {
display: inline-block;
height: 20px;
width: 100%;
border-radius: 4px;
background-color: #f5f6fa;
overflow: hidden;
position: relative;
&:before {
width: inherit;
height: inherit;
content: '';
position: absolute;
background: linear-gradient(to right, #f5f6fa 2%, #eee 18%, #f5f6fa 33%);
animation-duration: 1.7s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-name: tilePlaceholderAnimate;
background-size: 1300px;
}
}
.tile-label-loader {
height: 14px;
width: 80px;
}
.tile-address-loader {
& + .tile-address-loader {
margin-top: 6px;
}
}
@keyframes tilePlaceholderAnimate {
0%{ background-position: -650px 0; }
100% { background-position: 650px 0; }
}
// Loading Animation // Loading Animation
@keyframes playBlockLoadingAnimation { @keyframes playBlockLoadingAnimation {

@ -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 =
@ -115,4 +115,6 @@ defmodule BlockScoutWeb.AddressLogsController do
not_found(conn) not_found(conn)
end end
end end
def search_logs(conn, _), do: not_found(conn)
end end

@ -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",

@ -130,6 +130,8 @@ defmodule BlockScoutWeb.AddressTransactionController do
end end
end end
def token_transfers_csv(conn, _), do: not_found(conn)
def transactions_csv(conn, %{"address_id" => address_hash_string}) do def transactions_csv(conn, %{"address_id" => address_hash_string}) 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) do {:ok, address} <- Chain.hash_to_address(address_hash) do
@ -149,4 +151,6 @@ defmodule BlockScoutWeb.AddressTransactionController do
not_found(conn) not_found(conn)
end end
end end
def transactions_csv(conn, _), do: not_found(conn)
end end

@ -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
@ -52,6 +52,8 @@ defmodule BlockScoutWeb.ChainController do
end end
end end
def search(conn, _), do: not_found(conn)
def token_autocomplete(conn, %{"q" => term}) when is_binary(term) do def token_autocomplete(conn, %{"q" => term}) when is_binary(term) do
if term == "" do if term == "" do
json(conn, "{}") json(conn, "{}")
@ -72,9 +74,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:

@ -30,10 +30,12 @@ defmodule BlockScoutWeb.SmartContractController do
end end
end end
def index(conn, _), do: not_found(conn)
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 +53,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)

@ -24,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)

@ -261,6 +261,6 @@ defmodule BlockScoutWeb.Router do
get("/api_docs", APIDocsController, :index) get("/api_docs", APIDocsController, :index)
get("/eth_rpc_api_docs", APIDocsController, :eth_rpc) get("/eth_rpc_api_docs", APIDocsController, :eth_rpc)
get("/:page", PageNotFoundController, :index) get("/*path", PageNotFoundController, :index)
end end
end end

@ -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">

@ -38,7 +38,7 @@
</tr> </tr>
</thead> </thead>
<tbody data-items data-selector="top-addresses-list"> <tbody data-items data-selector="top-addresses-list">
<%= render BlockScoutWeb.CommonComponentsView, "_table-loader.html" %>
</tbody> </tbody>
</table> </table>
</div> </div>

@ -40,7 +40,9 @@
</div> </div>
</div> </div>
<div data-selector="coin-balances-list" data-items></div> <div data-selector="coin-balances-list" data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -66,7 +66,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -27,7 +27,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -19,7 +19,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<div class="transaction-bottom-panel"> <div class="transaction-bottom-panel">
<div csv-download class="download-all-transactions"> <div csv-download class="download-all-transactions">

@ -21,7 +21,9 @@
</span> </span>
</button> </button>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -65,7 +65,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<div class="transaction-bottom-panel"> <div class="transaction-bottom-panel">
<div class="download-all-transactions"> <div class="download-all-transactions">

@ -22,7 +22,9 @@
<%= gettext "Something went wrong, click to reload." %> <%= gettext "Something went wrong, click to reload." %>
</span> </span>
</button> </button>
<div data-items data-selector="validations-list"></div> <div data-items data-selector="validations-list">
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -11,7 +11,9 @@
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<div data-empty-response-message style="display: none;"> <div data-empty-response-message style="display: none;">
<span><%= gettext "There are no blocks." %></span> <span><%= gettext "There are no blocks." %></span>
</div> </div>

@ -29,7 +29,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -117,13 +117,7 @@
<%= gettext "Something went wrong, click to retry." %> <%= gettext "Something went wrong, click to retry." %>
</span> </span>
</button> </button>
<div hidden data-selector="loading-message" class="tile tile-muted text-center mt-3"> <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
<span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading...") %>
</div>
</span> </span>
</div> </div>
</div> </div>

@ -0,0 +1,85 @@
<tr class="table-content-pseudo">
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
</tr>
<tr class="table-content-pseudo">
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
</tr>
<tr class="table-content-pseudo">
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
</tr>
<tr class="table-content-pseudo">
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
</tr>
<tr class="table-content-pseudo">
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
<td class="stakes-td">
<span class="table-content-loader"></span>
</td>
</tr>

@ -0,0 +1,72 @@
<div 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">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="tile-status-label ml-2 ml-md-0">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
<div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0">
<span class="tile-loader tile-address-loader"></span>
<span class="tile-loader tile-address-loader"></span>
</div>
<div class="col-md-3 col-lg-2 d-flex flex-row flex-md-column flex-nowrap justify-content-center text-md-right mt-3 mt-md-0 tile-bottom">
<span class="mr-2 mr-md-0 order-1">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="mr-2 mr-md-0 order-2">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
</div>
</div>
<div 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">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="tile-status-label ml-2 ml-md-0">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
<div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0">
<span class="tile-loader tile-address-loader"></span>
<span class="tile-loader tile-address-loader"></span>
</div>
<div class="col-md-3 col-lg-2 d-flex flex-row flex-md-column flex-nowrap justify-content-center text-md-right mt-3 mt-md-0 tile-bottom">
<span class="mr-2 mr-md-0 order-1">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="mr-2 mr-md-0 order-2">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
</div>
</div>
<div 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">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="tile-status-label ml-2 ml-md-0">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
<div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0">
<span class="tile-loader tile-address-loader"></span>
<span class="tile-loader tile-address-loader"></span>
</div>
<div class="col-md-3 col-lg-2 d-flex flex-row flex-md-column flex-nowrap justify-content-center text-md-right mt-3 mt-md-0 tile-bottom">
<span class="mr-2 mr-md-0 order-1">
<span class="tile-loader tile-label-loader"></span>
</span>
<span class="mr-2 mr-md-0 order-2">
<span class="tile-loader tile-label-loader"></span>
</span>
</div>
</div>
</div>

@ -23,13 +23,8 @@
<%= gettext "There are no pending transactions." %> <%= gettext "There are no pending transactions." %>
</span> </span>
</div> </div>
<div data-items data-selector="transactions-pending-list"></div> <div data-items data-selector="transactions-pending-list">
<div data-loading-message class="tile tile-muted text-center mt-3" style="display: none;"> <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
<span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading") %>...
</div> </div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -28,7 +28,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -27,7 +27,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -26,7 +26,9 @@
</span> </span>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -28,7 +28,9 @@
</div> </div>
</div> </div>
<div data-selector="transactions-list" data-items></div> <div data-selector="transactions-list" data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -16,7 +16,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
</div> </div>

@ -19,7 +19,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -18,7 +18,9 @@
</div> </div>
</div> </div>
<div data-items></div> <div data-items>
<%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
</div>
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>

@ -1058,7 +1058,6 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:14 #: lib/block_scout_web/templates/address_read_contract/index.html.eex:14
#: lib/block_scout_web/templates/chain/show.html.eex:99 #: lib/block_scout_web/templates/chain/show.html.eex:99
#: lib/block_scout_web/templates/chain/show.html.eex:125
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:21 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:21
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
@ -1277,7 +1276,7 @@ msgid "There are no pending transactions."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/block/index.html.eex:16 #: lib/block_scout_web/templates/block/index.html.eex:18
msgid "There are no blocks." msgid "There are no blocks."
msgstr "" msgstr ""
@ -1745,8 +1744,8 @@ msgid "here."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26 #: lib/block_scout_web/templates/address_token/index.html.eex:28
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72 #: lib/block_scout_web/templates/address_transaction/index.html.eex:74
msgid "CSV" msgid "CSV"
msgstr "" msgstr ""

@ -1058,7 +1058,6 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:14 #: lib/block_scout_web/templates/address_read_contract/index.html.eex:14
#: lib/block_scout_web/templates/chain/show.html.eex:99 #: lib/block_scout_web/templates/chain/show.html.eex:99
#: lib/block_scout_web/templates/chain/show.html.eex:125
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:21 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:21
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
@ -1277,7 +1276,7 @@ msgid "There are no pending transactions."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/block/index.html.eex:16 #: lib/block_scout_web/templates/block/index.html.eex:18
msgid "There are no blocks." msgid "There are no blocks."
msgstr "" msgstr ""
@ -1745,8 +1744,8 @@ msgid "here."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26 #: lib/block_scout_web/templates/address_token/index.html.eex:28
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72 #: lib/block_scout_web/templates/address_transaction/index.html.eex:74
msgid "CSV" msgid "CSV"
msgstr "" msgstr ""

@ -353,4 +353,8 @@ defmodule EthereumJSONRPC.Transaction do
_ -> {key, quantity_to_integer(chain_id)} _ -> {key, quantity_to_integer(chain_id)}
end end
end end
defp entry_to_elixir(_) do
{nil, nil}
end
end end

@ -2,4 +2,14 @@ defmodule EthereumJSONRPC.TransactionTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
doctest EthereumJSONRPC.Transaction doctest EthereumJSONRPC.Transaction
alias EthereumJSONRPC.Transaction
describe "to_elixir/1" do
test "skips unsupported keys" do
map = %{"key" => "value", "key1" => "value1"}
assert %{nil: nil} = Transaction.to_elixir(map)
end
end
end end

@ -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

@ -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,14 +59,20 @@ 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 =
address_hash case mabi do
|> Chain.address_hash_to_smart_contract() nil ->
|> Map.get(:abi) address_hash
|> Chain.address_hash_to_smart_contract()
|> 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
contract_address_hash abi =
|> Chain.address_hash_to_smart_contract() contract_address_hash
|> Map.get(:abi, []) |> Chain.address_hash_to_smart_contract()
|> Enum.filter(& &1["constant"]) |> Map.get(:abi)
|> fetch_current_value_from_blockchain(contract_address_hash, [])
|> Enum.reverse()
end
def fetch_current_value_from_blockchain(
[%{"inputs" => []} = function | tail],
contract_address_hash,
acc
) do
values =
fetch_from_blockchain(contract_address_hash, %{
name: function["name"],
args: function["inputs"],
outputs: function["outputs"]
})
formatted = Map.replace!(function, "outputs", values) case abi do
nil ->
[]
fetch_current_value_from_blockchain(tail, contract_address_hash, [formatted | acc]) _ ->
abi
|> Enum.filter(& &1["constant"])
|> Enum.map(&fetch_current_value_from_blockchain(&1, abi, contract_address_hash))
end
end end
def fetch_current_value_from_blockchain([function | tail], contract_address_hash, acc) do defp fetch_current_value_from_blockchain(function, abi, contract_address_hash) do
values = link_outputs_and_values(%{}, Map.get(function, "outputs", []), function["name"]) values =
case function do
%{"inputs" => []} ->
name = function["name"]
args = function["inputs"]
outputs = function["outputs"]
contract_address_hash
|> query_verified_contract(%{name => normalize_args(args)}, abi)
|> link_outputs_and_values(outputs, name)
formatted = Map.replace!(function, "outputs", values) _ ->
link_outputs_and_values(%{}, Map.get(function, "outputs", []), function["name"])
end
fetch_current_value_from_blockchain(tail, contract_address_hash, [formatted | acc]) Map.replace!(function, "outputs", values)
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)
|> Enum.filter(fn function -> function["name"] == name end)
|> List.first() outputs =
case abi do
fetch_from_blockchain(contract_address_hash, %{ nil ->
name: name, nil
args: args,
outputs: function["outputs"] _ ->
}) function =
end abi
|> Enum.filter(fn function -> function["name"] == name end)
|> List.first()
function["outputs"]
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