Merge branch 'master' into besu-revert-reason

besu-revert-reason
Victor Baranov 4 years ago committed by GitHub
commit a27018a129
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .circleci/config.yml
  2. 4
      .dialyzer-ignore
  3. 8
      CHANGELOG.md
  4. 4
      apps/block_scout_web/assets/css/components/_card.scss
  5. 6
      apps/block_scout_web/assets/css/components/_dropdown.scss
  6. 15
      apps/block_scout_web/assets/css/components/_navbar.scss
  7. 14
      apps/block_scout_web/assets/js/lib/smart_contract/functions.js
  8. 29
      apps/block_scout_web/assets/js/lib/smart_contract/write.js
  9. 6
      apps/block_scout_web/assets/package-lock.json
  10. 2
      apps/block_scout_web/assets/package.json
  11. 4
      apps/block_scout_web/config/config.exs
  12. 30
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex
  13. 3
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex
  14. 72
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  15. 79
      apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex
  16. 1
      apps/block_scout_web/lib/block_scout_web/templates/icons/_external_link.html.eex
  17. 19
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  18. 37
      apps/block_scout_web/lib/block_scout_web/templates/log/_data_decoded_view.html.eex
  19. 5
      apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
  20. 42
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex
  21. 1
      apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex
  22. 80
      apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex
  23. 17
      apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/index.html.eex
  24. 21
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
  25. 15
      apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
  26. 3
      apps/block_scout_web/lib/block_scout_web/views/log_view.ex
  27. 82
      apps/block_scout_web/priv/gettext/default.pot
  28. 82
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  29. 326
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  30. 2
      apps/explorer/lib/explorer/chain.ex
  31. 32
      apps/explorer/lib/explorer/chain/log.ex
  32. 35
      apps/explorer/lib/explorer/chain/smart_contract.ex
  33. 50
      apps/explorer/lib/explorer/etherscan.ex
  34. 10
      apps/explorer/lib/explorer/smart_contract/publisher.ex
  35. 4
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  36. 10
      apps/explorer/lib/explorer/smart_contract/verifier.ex
  37. 35
      apps/explorer/test/explorer/chain/log_test.exs
  38. 150
      apps/explorer/test/explorer/etherscan_test.exs
  39. 6
      docker/Makefile

@ -233,7 +233,7 @@ jobs:
eslint: eslint:
docker: docker:
# Ensure .tool-versions matches # Ensure .tool-versions matches
- image: circleci/node:12.16.1-browsers-legacy - image: circleci/node:12.18.2-browsers-legacy
working_directory: ~/app working_directory: ~/app
@ -275,7 +275,7 @@ jobs:
jest: jest:
docker: docker:
# Ensure .tool-versions matches # Ensure .tool-versions matches
- image: circleci/node:12.16.1-browsers-legacy - image: circleci/node:12.18.2-browsers-legacy
working_directory: ~/app working_directory: ~/app

@ -6,6 +6,7 @@ apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex:400: Function timestamp_to_datetim
lib/explorer/repo/prometheus_logger.ex:8 lib/explorer/repo/prometheus_logger.ex:8
lib/block_scout_web/views/layout_view.ex:175 lib/block_scout_web/views/layout_view.ex:175
lib/explorer/smart_contract/publisher_worker.ex:6 lib/explorer/smart_contract/publisher_worker.ex:6
lib/explorer/smart_contract/verifier.ex:82
apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: Function microseconds_time/1 has no local return apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: Function microseconds_time/1 has no local return
apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: The call 'Elixir.System':convert_time_unit(__@1::any(),'native','microseconds') breaks the contract (integer(),time_unit() | 'native',time_unit() | 'native') -> integer() apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: The call 'Elixir.System':convert_time_unit(__@1::any(),'native','microseconds') breaks the contract (integer(),time_unit() | 'native',time_unit() | 'native') -> integer()
lib/block_scout_web/views/layout_view.ex:172: The call 'Elixir.Poison.Parser':'parse!'(any(),#{'keys':='atoms!'}) will never return since the success typing is (binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | []),[{atom(),_}]) -> 'false' | 'nil' | 'true' | binary() | ['false' | 'nil' | 'true' | binary() | [any()] | number() | map()] | number() | map() and the contract is (iodata(),'Elixir.Keyword':t()) -> t() lib/block_scout_web/views/layout_view.ex:172: The call 'Elixir.Poison.Parser':'parse!'(any(),#{'keys':='atoms!'}) will never return since the success typing is (binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | []),[{atom(),_}]) -> 'false' | 'nil' | 'true' | binary() | ['false' | 'nil' | 'true' | binary() | [any()] | number() | map()] | number() | map() and the contract is (iodata(),'Elixir.Keyword':t()) -> t()
@ -13,6 +14,7 @@ apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The pattern 'fa
apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The test 5 == 'infinity' can never evaluate to 'true' apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The test 5 == 'infinity' can never evaluate to 'true'
lib/block_scout_web/router.ex:1 lib/block_scout_web/router.ex:1
lib/phoenix/router.ex:324 lib/phoenix/router.ex:324
lib/block_scout_web/views/layout_view.ex:143 lib/block_scout_web/views/layout_view.ex:138: The call 'Elixir.Poison.Parser':'parse!'
lib/block_scout_web/views/layout_view.ex:224: The call 'Elixir.Poison.Parser':'parse!'
lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:21 lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:21
lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:22 lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:22

@ -1,8 +1,16 @@
## Current ## Current
### Features ### Features
- [#3199](https://github.com/poanetwork/blockscout/pull/3199) - Show compilation error at contract verification
- [#3193](https://github.com/poanetwork/blockscout/pull/3193) - Raw trace copy button
- [#3184](https://github.com/poanetwork/blockscout/pull/3184) - Apps navbar menu item
- [#3145](https://github.com/poanetwork/blockscout/pull/3145) - Pending txs per address API endpoint
### Fixes ### Fixes
- [#3201](https://github.com/poanetwork/blockscout/pull/3201) - Connect to Metamask button
- [#3192](https://github.com/poanetwork/blockscout/pull/3192) - Dropdown menu doesn't open at "not found" page
- [#3190](https://github.com/poanetwork/blockscout/pull/3190) - Contract log/method decoded view improvements: eliminate horizontal scroll, remove excess borders, whitespaces
- [#3185](https://github.com/poanetwork/blockscout/pull/3185) - Transaction page: decoding logs from nested contracts calls
- [#3182](https://github.com/poanetwork/blockscout/pull/3182) - Besu: support revertReason key in eth_getTransactionReceipt endpoint - [#3182](https://github.com/poanetwork/blockscout/pull/3182) - Besu: support revertReason key in eth_getTransactionReceipt endpoint
- [#3178](https://github.com/poanetwork/blockscout/pull/3178) - Fix permanent fetching tokens... when read/write proxy tab is active - [#3178](https://github.com/poanetwork/blockscout/pull/3178) - Fix permanent fetching tokens... when read/write proxy tab is active
- [#3178](https://github.com/poanetwork/blockscout/pull/3178) - Fix unavailable navbar menu when read/write proxy tab is active - [#3178](https://github.com/poanetwork/blockscout/pull/3178) - Fix unavailable navbar menu when read/write proxy tab is active

@ -258,6 +258,10 @@ $card-tab-icon-color-active: #fff !default;
} }
} }
.implementation-container {
margin-top: 10px;
}
.implementation-title { .implementation-title {
float: left; float: left;
margin-right: 5px; margin-right: 5px;

@ -42,6 +42,12 @@ $dropdown-menu-item-hover-background: rgba($secondary, 0.1) !default;
&:focus { &:focus {
background-color: $dropdown-menu-item-hover-background; background-color: $dropdown-menu-item-hover-background;
color: $dropdown-menu-item-hover-color; color: $dropdown-menu-item-hover-color;
.external-link-icon {
path {
fill: $header-icon-color-hover;
}
}
} }
} }

@ -260,3 +260,18 @@ $navbar-logo-width: auto !default;
transition: none !important; transition: none !important;
} }
} }
.external-link-icon {
float: right;
margin-top: -1px;
& {
&.active,
&:hover,
&:focus {
path {
fill: $header-icon-color-hover;
}
}
}
}

@ -1,6 +1,6 @@
import $ from 'jquery' import $ from 'jquery'
import ethNetProps from 'eth-net-props' import ethNetProps from 'eth-net-props'
import { walletEnabled, getCurrentAccount } from './write.js' import { walletEnabled, connectToWallet, getCurrentAccount, hideConnectButton } from './write.js'
import { openErrorModal, openWarningModal, openSuccessModal, openModalWithMessage } from '../modals.js' import { openErrorModal, openWarningModal, openSuccessModal, openModalWithMessage } from '../modals.js'
import '../../pages/address' import '../../pages/address'
@ -19,6 +19,18 @@ const loadFunctions = (element) => {
response => $element.html(response) response => $element.html(response)
) )
.done(function () { .done(function () {
const $connect = $('[connect-metamask]')
if (hideConnectButton()) {
$connect.addClass('hidden')
} else {
$connect.removeClass('hidden')
}
$connect.on('click', () => {
connectToWallet()
})
$('[data-function]').each((_, element) => { $('[data-function]').each((_, element) => {
readWriteFunction(element) readWriteFunction(element)
}) })

@ -3,10 +3,10 @@ import Web3 from 'web3'
export const walletEnabled = () => { export const walletEnabled = () => {
if (window.ethereum) { if (window.ethereum) {
window.web3 = new Web3(window.ethereum) window.web3 = new Web3(window.ethereum)
if (window.ethereum._state && window.ethereum._state.isUnlocked) { // Nifty Wallet if (window.ethereum.isUnlocked && window.ethereum.isNiftyWallet) { // Nifty Wallet
window.web3 = new Web3(window.web3.currentProvider) window.web3 = new Web3(window.web3.currentProvider)
return Promise.resolve(true) return Promise.resolve(true)
} else if (window.ethereum._state && window.ethereum._state.isUnlocked === false) { // Nifty Wallet } else if (window.ethereum.isUnlocked === false && window.ethereum.isNiftyWallet) { // Nifty Wallet
return Promise.resolve(false) return Promise.resolve(false)
} else { } else {
window.ethereum.enable() window.ethereum.enable()
@ -21,9 +21,34 @@ export const walletEnabled = () => {
} }
} }
export const connectToWallet = () => {
if (window.ethereum) {
window.ethereum.enable()
}
}
export const getCurrentAccount = async () => { export const getCurrentAccount = async () => {
const accounts = await window.web3.eth.getAccounts() const accounts = await window.web3.eth.getAccounts()
const account = accounts[0] ? accounts[0].toLowerCase() : null const account = accounts[0] ? accounts[0].toLowerCase() : null
return account return account
} }
export const hideConnectButton = () => {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum)
if (window.ethereum.isNiftyWallet) {
return true
} else if (window.ethereum.isMetaMask) {
if (window.ethereum.selectedAddress) {
return true
} else {
return false
}
} else {
return true
}
} else {
return false
}
}

@ -8906,9 +8906,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.15", "version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
}, },
"lodash.memoize": { "lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",

@ -30,7 +30,7 @@
"highlightjs-solidity": "^1.0.8", "highlightjs-solidity": "^1.0.8",
"humps": "^2.0.1", "humps": "^2.0.1",
"jquery": "^3.4.0", "jquery": "^3.4.0",
"lodash": "^4.17.15", "lodash": "^4.17.19",
"moment": "^2.24.0", "moment": "^2.24.0",
"nanomorph": "^5.4.0", "nanomorph": "^5.4.0",
"numeral": "^2.0.6", "numeral": "^2.0.6",

@ -32,7 +32,9 @@ config :block_scout_web,
}, },
other_networks: System.get_env("SUPPORTED_CHAINS"), other_networks: System.get_env("SUPPORTED_CHAINS"),
webapp_url: System.get_env("WEBAPP_URL"), webapp_url: System.get_env("WEBAPP_URL"),
api_url: System.get_env("API_URL") api_url: System.get_env("API_URL"),
apps_menu: if(System.get_env("APPS_MENU", "false") == "true", do: true, else: false),
external_apps: System.get_env("EXTERNAL_APPS")
config :block_scout_web, BlockScoutWeb.Counters.BlocksIndexedCounter, enabled: true config :block_scout_web, BlockScoutWeb.Counters.BlocksIndexedCounter, enabled: true

@ -71,6 +71,29 @@ defmodule BlockScoutWeb.API.RPC.AddressController do
balance(conn, params, :balancemulti) balance(conn, params, :balancemulti)
end end
def pendingtxlist(conn, params) do
options = optional_params(params)
with {:address_param, {:ok, address_param}} <- fetch_address(params),
{:format, {:ok, address_hash}} <- to_address_hash(address_param),
{:ok, transactions} <- list_pending_transactions(address_hash, options) do
render(conn, :pendingtxlist, %{transactions: transactions})
else
{:address_param, :error} ->
conn
|> put_status(200)
|> render(:error, error: "Query parameter 'address' is required")
{:format, :error} ->
conn
|> put_status(200)
|> render(:error, error: "Invalid address format")
{:error, :not_found} ->
render(conn, :error, error: "No transactions found", data: [])
end
end
def txlist(conn, params) do def txlist(conn, params) do
options = optional_params(params) options = optional_params(params)
@ -457,6 +480,13 @@ defmodule BlockScoutWeb.API.RPC.AddressController do
end end
end end
defp list_pending_transactions(address_hash, options) do
case Etherscan.list_pending_transactions(address_hash, options) do
[] -> {:error, :not_found}
pending_transactions -> {:ok, pending_transactions}
end
end
defp list_internal_transactions(transaction_hash) do defp list_internal_transactions(transaction_hash) do
case Etherscan.list_internal_transactions(transaction_hash) do case Etherscan.list_internal_transactions(transaction_hash) do
[] -> {:error, :not_found} [] -> {:error, :not_found}

@ -58,7 +58,8 @@ defmodule BlockScoutWeb.SmartContractController do
contract_abi: contract_abi, contract_abi: contract_abi,
implementation_address: implementation_address_hash_string, implementation_address: implementation_address_hash_string,
implementation_abi: implementation_abi, implementation_abi: implementation_abi,
contract_type: contract_type contract_type: contract_type,
action: action
) )
else else
:error -> :error ->

@ -37,6 +37,27 @@ defmodule BlockScoutWeb.Etherscan do
] ]
} }
@account_pendingtxlist_example_value %{
"status" => "1",
"message" => "OK",
"result" => [
%{
"hash" => "0x98beb27135aa0a25650557005ad962919d6a278c4b3dde7f4f6a3a1e65aa746c",
"nonce" => "0",
"from" => "0x3fb1cd2cd96c6d5c0b5eb3322d807b34482481d4",
"to" => "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
"value" => "0",
"gas" => "122261",
"gasPrice" => "50000000000",
"input" =>
"0xf00d4b5d000000000000000000000000036c8cecce8d8bbf0831d840d7f29c9e3ddefa63000000000000000000000000c5a96db085dda36ffbe390f455315d30d6d3dc52",
"contractAddress" => "",
"cumulativeGasUsed" => "122207",
"gasUsed" => "122207"
}
]
}
@account_txlist_example_value %{ @account_txlist_example_value %{
"status" => "1", "status" => "1",
"message" => "OK", "message" => "OK",
@ -1195,6 +1216,56 @@ defmodule BlockScoutWeb.Etherscan do
] ]
} }
@account_pendingtxlist_action %{
name: "pendingtxlist",
description: "Get pending transactions by address.",
required_params: [
%{
key: "address",
placeholder: "addressHash",
type: "string",
description: "A 160-bit code used for identifying Accounts."
}
],
optional_params: [
%{
key: "page",
type: "integer",
description:
"A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction."
},
%{
key: "offset",
type: "integer",
description:
"A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction."
}
],
responses: [
%{
code: "200",
description: "successful operation",
example_value: Jason.encode!(@account_pendingtxlist_example_value),
model: %{
name: "Result",
fields: %{
status: @status_type,
message: @message_type,
result: %{
type: "array",
array_type: @transaction
}
}
}
},
%{
code: "200",
description: "error",
example_value: Jason.encode!(@account_txlist_example_value_error)
}
]
}
@account_txlist_action %{ @account_txlist_action %{
name: "txlist", name: "txlist",
description: description:
@ -2351,6 +2422,7 @@ defmodule BlockScoutWeb.Etherscan do
@account_eth_get_balance_action, @account_eth_get_balance_action,
@account_balance_action, @account_balance_action,
@account_balancemulti_action, @account_balancemulti_action,
@account_pendingtxlist_action,
@account_txlist_action, @account_txlist_action,
@account_txlistinternal_action, @account_txlistinternal_action,
@account_tokentx_action, @account_tokentx_action,

@ -46,45 +46,7 @@
<td colspan="3"><code><%= text %></code></td> <td colspan="3"><code><%= text %></code></td>
</tr> </tr>
</table> </table>
<div class="table-responsive text-center"> <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %>
<table style="color: black;" summary="<%= gettext "Log Data" %>" class="table thead-light table-bordered">
<tr>
<th scope="col"></th>
<th scope="col"><%= gettext "Name" %></th>
<th scope="col"><%= gettext "Type" %></th>
<th scope="col"><%= gettext "Indexed?" %></th>
<th scope="col"><%= gettext "Data" %></th>
<tr>
<%= for {name, type, indexed?, value} <- mapping do %>
<tr>
<th scope="row">
<%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<span
aria-label='<%= gettext "Copy Value" %>'
class="btn-copy-ico"
data-clipboard-text="<%= copy_text %>"
data-placement="top"
data-toggle="tooltip"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="32" height="32">
<path fill-rule="evenodd" d="M23.5 20.5a1 1 0 0 1-1-1v-9h-9a1 1 0 0 1 0-2h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-3-7v10a1 1 0 0 1-1 1h-10a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1zm-2 1h-8v8h8v-8z"/>
</svg>
</span>
<% end %>
</th>
<td><%= name %></td>
<td><%= type %></td>
<td><%= indexed? %></td>
<td>
<pre class="transaction-input-text tile"><code><%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %></code></pre>
</td>
</tr>
<% end %>
</table>
</div>
<% {:error, :contract_not_verified, results} -> %> <% {:error, :contract_not_verified, results} -> %>
<%= for {:ok, method_id, text, mapping} <- results do %> <%= for {:ok, method_id, text, mapping} <- results do %>
<dt class="col-md-2"><%= gettext "Decoded" %></dt> <dt class="col-md-2"><%= gettext "Decoded" %></dt>
@ -99,44 +61,7 @@
<td colspan="3"><code><%= text %></code></td> <td colspan="3"><code><%= text %></code></td>
</tr> </tr>
</table> </table>
<div class="table-responsive text-center"> <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %>
<table style="color: black;" summary="<%= gettext "Log Data" %>" class="table thead-light table-bordered">
<tr>
<th scope="col"></th>
<th scope="col"><%= gettext "Name" %></th>
<th scope="col"><%= gettext "Type" %></th>
<th scope="col"><%= gettext "Indexed?" %></th>
<th scope="col"><%= gettext "Data" %></th>
<tr>
<%= for {name, type, indexed?, value} <- mapping do %>
<tr>
<th scope="row">
<%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<span
aria-label='<%= gettext "Copy Value" %>'
class="btn-copy-ico"
data-clipboard-text="<%= copy_text %>"
data-placement="top"
data-toggle="tooltip"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="32" height="32">
<path fill-rule="evenodd" d="M23.5 20.5a1 1 0 0 1-1-1v-9h-9a1 1 0 0 1 0-2h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-3-7v10a1 1 0 0 1-1 1h-10a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1zm-2 1h-8v8h8v-8z"/>
</svg>
</span>
<% end %>
</th>
<td><%= name %></td>
<td><%= type %></td>
<td><%= indexed? %></td>
<td>
<pre class="transaction-input-text tile"><code><%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %></code></pre>
</td>
</tr>
<% end %>
</table>
</div> </div>
<% end %> <% end %>
<% _ -> %> <% _ -> %>

@ -0,0 +1 @@
<svg height="12pt" viewBox="0 0 12 12" width="12pt" xmlns="http://www.w3.org/2000/svg"><path d="m9.992188 1.496094c-.019532 0-.039063 0-.058594.003906h-2.433594c-.179688-.003906-.347656.09375-.4375.246094-.09375.15625-.09375.351562 0 .507812.089844.152344.257812.25.4375.246094h1.292969l-4.644531 4.648438c-.132813.125-.183594.308593-.140626.484374.046876.175782.183594.3125.359376.359376.175781.042968.359374-.007813.488281-.136719l4.644531-4.648438v1.292969c-.003906.179688.09375.347656.246094.4375.15625.09375.351562.09375.507812 0 .152344-.089844.25-.257812.246094-.4375v-2.4375c.019531-.144531-.023438-.292969-.125-.402344-.097656-.109375-.238281-.167968-.382812-.164062zm-7.492188.003906c-.546875 0-1 .453125-1 1v7c0 .546875.453125 1 1 1h7c.546875 0 1-.453125 1-1v-3c.003906-.179688-.09375-.347656-.246094-.4375-.15625-.09375-.351562-.09375-.507812 0-.152344.089844-.25.257812-.246094.4375v3h-7v-7h3c.179688.003906.347656-.09375.4375-.246094.09375-.15625.09375-.351562 0-.507812-.089844-.152344-.257812-.25-.4375-.246094zm0 0" fill="#333"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -103,6 +103,25 @@
</div> </div>
</li> </li>
<% end %> <% end %>
<%= if Application.get_env(:block_scout_web, :apps_menu) == true do %>
<li class="nav-item dropdown">
<a href="#" role="button" id="navbarAppsDropdown" class="nav-link topnav-nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_apps_icon.html" %>
</span>
<%= gettext("Apps") %>
</a>
<div class="dropdown-menu" aria-labeledby="navbarAppsDropdown">
<%= for %{url: url, title: title} <- external_apps_list() do %>
<a href="<%= url%>" class="dropdown-item" target="_blank"><%= title %>
<span class="external-link-icon">
<%= render BlockScoutWeb.IconsView, "_external_link.html" %>
</span>
</a>
<% end %>
</div>
</li>
<% end %>
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link topnav-nav-link active-icon <%= if dropdown_nets() != [], do: "dropdown-toggle js-show-network-selector" %>" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link topnav-nav-link active-icon <%= if dropdown_nets() != [], do: "dropdown-toggle js-show-network-selector" %>" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon"> <span class="nav-link-icon">

@ -0,0 +1,37 @@
<div class="table-responsive text-center">
<table style="color: black;table-layout: fixed;" summary="<%= gettext "Log Data" %>" class="table thead-light table-bordered">
<tr>
<th scope="col" style="width: 110px;"><%= gettext "Name" %></th>
<th scope="col" style="width: 100px;"><%= gettext "Type" %></th>
<th scope="col" style="width: 75px;"><%= gettext "Indexed?" %></th>
<th scope="col"><%= gettext "Data" %></th>
<tr>
<%= for {name, type, indexed?, value} <- @mapping do %>
<tr>
<td><%= name %></td>
<td><%= type %></td>
<td><%= indexed? %></td>
<td align=left>
<%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<span
aria-label='<%= gettext "Copy Value" %>'
class="btn-copy-ico"
data-clipboard-text="<%= copy_text %>"
data-placement="top"
data-toggle="tooltip"
style="float: left;height: 20px;"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="25" height="25">
<path fill-rule="evenodd" d="M23.5 20.5a1 1 0 0 1-1-1v-9h-9a1 1 0 0 1 0-2h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-3-7v10a1 1 0 0 1-1 1h-10a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1zm-2 1h-8v8h8v-8z"/>
</svg>
</span>
<% end %>
<pre class="transaction-input-text pre-wrap"><code><%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %></code></pre>
</td>
</tr>
<% end %>
</table>
</div>

@ -1,5 +1,8 @@
<%= if @action== "write" do %>
<button connect-metamask class="button btn-line">Connect to Metamask</button>
<% end %>
<%= if @contract_type == "proxy" do %> <%= if @contract_type == "proxy" do %>
<div> <div class="implementation-container">
<h2 class="implementation-title">Implementation address: </h2><h3 class="implementation-value"><%= link( <h2 class="implementation-title">Implementation address: </h2><h3 class="implementation-value"><%= link(
@implementation_address, @implementation_address,
to: address_path(@conn, :show, @implementation_address) to: address_path(@conn, :show, @implementation_address)

@ -15,41 +15,39 @@
<div class="table-responsive text-center"> <div class="table-responsive text-center">
<table style="color: black; table-layout: fixed;" summary="<%= gettext "Transaction Inputs" %>" class="table thead-light table-bordered"> <table style="color: black; table-layout: fixed;" summary="<%= gettext "Transaction Inputs" %>" class="table thead-light table-bordered">
<tr> <tr>
<th scope="col" style="width: 60px;"></th>
<th scope="col" style="width: 10%;"><%= gettext "Name" %></th> <th scope="col" style="width: 10%;"><%= gettext "Name" %></th>
<th scope="col" style="width: 10%;"><%= gettext "Type" %></th> <th scope="col" style="width: 10%;"><%= gettext "Type" %></th>
<th scope="col"><%= gettext "Data" %></th> <th scope="col"><%= gettext "Data" %></th>
<tr> <tr>
<%= for {name, type, value} <- @mapping do %> <%= for {name, type, value} <- @mapping do %>
<tr> <tr>
<th scope="row">
<%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<span
aria-label='<%= gettext "Copy Value" %>'
class="btn-copy-ico"
data-clipboard-text='<%= copy_text %>'
data-placement="top"
data-toggle="tooltip"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="32" height="32">
<path fill-rule="evenodd" d="M23.5 20.5a1 1 0 0 1-1-1v-9h-9a1 1 0 0 1 0-2h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-3-7v10a1 1 0 0 1-1 1h-10a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1zm-2 1h-8v8h8v-8z"/>
</svg>
</span>
<% end %>
</th>
<td><%= name %></td> <td><%= name %></td>
<td><%= type %></td> <td><%= type %></td>
<td> <td align=left>
<%= case BlockScoutWeb.ABIEncodedValueView.value_html(type, value) do %> <%= case BlockScoutWeb.ABIEncodedValueView.value_html(type, value) do %>
<% :error -> %> <% :error -> %>
<div class="alert alert-danger"> <div class="alert alert-danger">
<%= gettext "Error rendering value" %> <%= gettext "Error rendering value" %>
</div> </div>
<% value -> %> <% _value -> %>
<pre class="transaction-input-text tile pre-wrap"><code><%= value %></code></pre> <%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<span
aria-label='<%= gettext "Copy Value" %>'
class="btn-copy-ico"
data-clipboard-text='<%= copy_text %>'
data-placement="top"
data-toggle="tooltip"
style="float: left;height: 20px;"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="25" height="25">
<path fill-rule="evenodd" d="M23.5 20.5a1 1 0 0 1-1-1v-9h-9a1 1 0 0 1 0-2h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-3-7v10a1 1 0 0 1-1 1h-10a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1zm-2 1h-8v8h8v-8z"/>
</svg>
</span>
<% end %>
<pre class="transaction-input-text pre-wrap" style="margin-bottom: 0px;"><code style="line-height: 25px;"><%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %></code></pre>
<% end %> <% end %>
</td> </td>
</tr> </tr>

@ -30,4 +30,5 @@
<a class="error-btn btn-line" href="/"><%= gettext("Back Home") %></a> <a class="error-btn btn-line" href="/"><%= gettext("Back Home") %></a>
</div> </div>
</div> </div>
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/transaction.js") %>"></script>
</section> </section>

@ -49,45 +49,7 @@
<td colspan="3"><code><%= text %></code></td> <td colspan="3"><code><%= text %></code></td>
</tr> </tr>
</table> </table>
<div class="table-responsive text-center"> <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %>
<table style="color: black;" summary="<%= gettext "Log Data" %>" class="table thead-light table-bordered">
<tr>
<th scope="col"></th>
<th scope="col"><%= gettext "Name" %></th>
<th scope="col"><%= gettext "Type" %></th>
<th scope="col"><%= gettext "Indexed?" %></th>
<th scope="col"><%= gettext "Data" %></th>
<tr>
<%= for {name, type, indexed?, value} <- mapping do %>
<tr>
<th scope="row">
<%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<span
aria-label='<%= gettext "Copy Value" %>'
class="btn-copy-ico"
data-clipboard-text="<%= copy_text %>"
data-placement="top"
data-toggle="tooltip"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="32" height="32">
<path fill-rule="evenodd" d="M23.5 20.5a1 1 0 0 1-1-1v-9h-9a1 1 0 0 1 0-2h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-3-7v10a1 1 0 0 1-1 1h-10a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1zm-2 1h-8v8h8v-8z"/>
</svg>
</span>
<% end %>
</th>
<td><%= name %></td>
<td><%= type %></td>
<td><%= indexed? %></td>
<td>
<pre class="transaction-input-text tile"><code><%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %></code></pre>
</td>
</tr>
<% end %>
</table>
</div>
<% {:error, :contract_not_verified, results} -> %> <% {:error, :contract_not_verified, results} -> %>
<%= for {:ok, method_id, text, mapping} <- results do %> <%= for {:ok, method_id, text, mapping} <- results do %>
<dt class="col-md-2"><%= gettext "Decoded" %></dt> <dt class="col-md-2"><%= gettext "Decoded" %></dt>
@ -102,45 +64,7 @@
<td colspan="3"><code><%= text %></code></td> <td colspan="3"><code><%= text %></code></td>
</tr> </tr>
</table> </table>
<div class="table-responsive text-center"> <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %>
<table style="color: black;" summary="<%= gettext "Log Data" %>" class="table thead-light table-bordered">
<tr>
<th scope="col"></th>
<th scope="col"><%= gettext "Name" %></th>
<th scope="col"><%= gettext "Type" %></th>
<th scope="col"><%= gettext "Indexed?" %></th>
<th scope="col"><%= gettext "Data" %></th>
<tr>
<%= for {name, type, indexed?, value} <- mapping do %>
<tr>
<th scope="row">
<%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %>
<% :error -> %>
<%= nil %>
<% copy_text -> %>
<span
aria-label='<%= gettext "Copy Value" %>'
class="btn-copy-ico"
data-clipboard-text="<%= copy_text %>"
data-placement="top"
data-toggle="tooltip"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="32" height="32">
<path fill-rule="evenodd" d="M23.5 20.5a1 1 0 0 1-1-1v-9h-9a1 1 0 0 1 0-2h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-3-7v10a1 1 0 0 1-1 1h-10a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1zm-2 1h-8v8h8v-8z"/>
</svg>
</span>
<% end %>
</th>
<td><%= name %></td>
<td><%= type %></td>
<td><%= indexed? %></td>
<td>
<pre class="transaction-input-text tile"><code><%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %></code></pre>
</td>
</tr>
<% end %>
</table>
</div>
<% end %> <% end %>
<% _ -> %> <% _ -> %>
<%= nil %> <%= nil %>

@ -4,7 +4,22 @@
<div class="card"> <div class="card">
<%= render BlockScoutWeb.TransactionView, "_tabs.html", assigns %> <%= render BlockScoutWeb.TransactionView, "_tabs.html", assigns %>
<div class="card-body"> <div class="card-body">
<h2 class="card-title"><%= gettext "Raw Trace" %></h2> <h2 class="card-title"><%= gettext "Raw Trace" %>
<span
aria-label="Copy Value"
class="btn-copy-icon tx-raw-input transaction-input"
id="tx-raw-input"
data-clipboard-text="<%= for {line, _} <- raw_traces_with_lines(@internal_transactions), do: line %>"
data-placement="top"
data-toggle="tooltip"
title='<%= gettext("Copy Raw Trace") %>'
style="float: right;"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.5 32.5" width="32" height="32">
<path fill-rule="evenodd" d="M23.5 20.5a1 1 0 0 1-1-1v-9h-9a1 1 0 0 1 0-2h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-3-7v10a1 1 0 0 1-1 1h-10a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1zm-2 1h-8v8h8v-8z"/>
</svg>
</span>
</h2>
<%= if Enum.count(@internal_transactions) > 0 do %> <%= if Enum.count(@internal_transactions) > 0 do %>
<pre class="pre-scrollable line-numbers" data-activate-highlight><code class="json "><%= for {line, number} <- raw_traces_with_lines(@internal_transactions) do %><div data-line-number="<%= number %>"><%= line %></div><% end %></code></pre> <pre class="pre-scrollable line-numbers" data-activate-highlight><code class="json "><%= for {line, number} <- raw_traces_with_lines(@internal_transactions) do %><div data-line-number="<%= number %>"><%= line %></div><% end %></code></pre>
<% else %> <% else %>

@ -22,6 +22,11 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
RPCView.render("show.json", data: data) RPCView.render("show.json", data: data)
end end
def render("pendingtxlist.json", %{transactions: transactions}) do
data = Enum.map(transactions, &prepare_pending_transaction/1)
RPCView.render("show.json", data: data)
end
def render("txlist.json", %{transactions: transactions}) do def render("txlist.json", %{transactions: transactions}) do
data = Enum.map(transactions, &prepare_transaction/1) data = Enum.map(transactions, &prepare_transaction/1)
RPCView.render("show.json", data: data) RPCView.render("show.json", data: data)
@ -79,6 +84,22 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
} }
end end
defp prepare_pending_transaction(transaction) do
%{
"hash" => "#{transaction.hash}",
"nonce" => "#{transaction.nonce}",
"from" => "#{transaction.from_address_hash}",
"to" => "#{transaction.to_address_hash}",
"value" => "#{transaction.value.value}",
"gas" => "#{transaction.gas}",
"gasPrice" => "#{transaction.gas_price.value}",
"input" => "#{transaction.input}",
"contractAddress" => "#{transaction.created_contract_address_hash}",
"cumulativeGasUsed" => "#{transaction.cumulative_gas_used}",
"gasUsed" => "#{transaction.gas_used}"
}
end
defp prepare_transaction(transaction) do defp prepare_transaction(transaction) do
%{ %{
"blockNumber" => "#{transaction.block_number}", "blockNumber" => "#{transaction.block_number}",

@ -216,6 +216,21 @@ defmodule BlockScoutWeb.LayoutView do
end end
end end
def external_apps_list do
if Application.get_env(:block_scout_web, :external_apps) do
try do
:block_scout_web
|> Application.get_env(:external_apps)
|> Parser.parse!(%{keys: :atoms!})
rescue
_ ->
[]
end
else
[]
end
end
defp validate_url(url) when is_binary(url) do defp validate_url(url) when is_binary(url) do
case URI.parse(url) do case URI.parse(url) do
%URI{host: nil} -> :error %URI{host: nil} -> :error

@ -0,0 +1,3 @@
defmodule BlockScoutWeb.LogView do
use BlockScoutWeb, :view
end

@ -448,11 +448,8 @@ msgid "Difficulty"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:66 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:20
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:119 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:38
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:31
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:69
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:122
msgid "Copy Value" msgid "Copy Value"
msgstr "" msgstr ""
@ -478,23 +475,20 @@ msgid "Curl"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:56 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:100
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:109 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:175 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:103
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:59
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:112
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:179
msgid "Data" msgid "Data"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:31 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:31
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:37 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:37
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:90 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:52
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:32 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:32
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:40 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:40
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:93 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:55
msgid "Decoded" msgid "Decoded"
msgstr "" msgstr ""
@ -568,8 +562,8 @@ msgid "ERC-721 "
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:40 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:75 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:78
msgid "ETH" msgid "ETH"
msgstr "" msgstr ""
@ -605,7 +599,7 @@ msgid "Enter the Solidity Contract Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:49 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:30
msgid "Error rendering value" msgid "Error rendering value"
msgstr "" msgstr ""
@ -933,10 +927,7 @@ msgid "If you have just submitted this transaction please wait for at least 30 s
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:55 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:6
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:108
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:58
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:111
msgid "Indexed?" msgid "Indexed?"
msgstr "" msgstr ""
@ -1025,10 +1016,7 @@ msgid "Loading...."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:50 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:2
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:103
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:53
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:106
msgid "Log Data" msgid "Log Data"
msgstr "" msgstr ""
@ -1089,13 +1077,10 @@ msgid "Must be set to:"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:53
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:106
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:59 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:59
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:4
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:18
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:109
msgid "Name" msgid "Name"
msgstr "" msgstr ""
@ -1195,7 +1180,7 @@ msgid "QR Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:44 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:47
msgid "Query" msgid "Query"
msgstr "" msgstr ""
@ -1258,14 +1243,14 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14 #: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:145 #: lib/block_scout_web/templates/layout/_topnav.html.eex:164
#: lib/block_scout_web/templates/layout/_topnav.html.eex:162 #: lib/block_scout_web/templates/layout/_topnav.html.eex:181
msgid "Search" msgid "Search"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:139 #: lib/block_scout_web/templates/layout/_topnav.html.eex:158
#: lib/block_scout_web/templates/layout/_topnav.html.eex:143 #: lib/block_scout_web/templates/layout/_topnav.html.eex:162
msgid "Search by address, token symbol name, transaction hash, or block number" msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr "" msgstr ""
@ -1455,8 +1440,8 @@ msgid "Topic"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:145 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:70
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:149 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:73
msgid "Topics" msgid "Topics"
msgstr "" msgstr ""
@ -1513,11 +1498,8 @@ msgid "Twitter"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:54 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:5
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:57
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:110
msgid "Type" msgid "Type"
msgstr "" msgstr ""
@ -1649,7 +1631,7 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:74 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:77
msgid "WEI" msgid "WEI"
msgstr "" msgstr ""
@ -1776,7 +1758,7 @@ msgid "Loading chart"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:189 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:113
msgid "Log Index" msgid "Log Index"
msgstr "" msgstr ""
@ -1937,7 +1919,7 @@ msgid "Waiting for transaction's confirmation..."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:44 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:47
msgid "Write" msgid "Write"
msgstr "" msgstr ""
@ -1946,3 +1928,13 @@ msgstr ""
#: lib/block_scout_web/views/address_view.ex:350 #: lib/block_scout_web/views/address_view.ex:350
msgid "Write Proxy" msgid "Write Proxy"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:112
msgid "Apps"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:15
msgid "Copy Raw Trace"
msgstr ""

@ -448,11 +448,8 @@ msgid "Difficulty"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:66 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:20
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:119 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:38
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:31
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:69
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:122
msgid "Copy Value" msgid "Copy Value"
msgstr "" msgstr ""
@ -478,23 +475,20 @@ msgid "Curl"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:56 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:100
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:109 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:175 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:103
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:59
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:112
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:179
msgid "Data" msgid "Data"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:31 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:31
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:37 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:37
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:90 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:52
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:32 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:32
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:40 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:40
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:93 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:55
msgid "Decoded" msgid "Decoded"
msgstr "" msgstr ""
@ -568,8 +562,8 @@ msgid "ERC-721 "
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:40 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:75 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:78
msgid "ETH" msgid "ETH"
msgstr "" msgstr ""
@ -605,7 +599,7 @@ msgid "Enter the Solidity Contract Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:49 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:30
msgid "Error rendering value" msgid "Error rendering value"
msgstr "" msgstr ""
@ -933,10 +927,7 @@ msgid "If you have just submitted this transaction please wait for at least 30 s
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:55 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:6
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:108
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:58
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:111
msgid "Indexed?" msgid "Indexed?"
msgstr "" msgstr ""
@ -1025,10 +1016,7 @@ msgid "Loading...."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:50 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:2
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:103
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:53
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:106
msgid "Log Data" msgid "Log Data"
msgstr "" msgstr ""
@ -1089,13 +1077,10 @@ msgid "Must be set to:"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:53
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:106
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:59 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:59
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:4
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:18
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:109
msgid "Name" msgid "Name"
msgstr "" msgstr ""
@ -1195,7 +1180,7 @@ msgid "QR Code"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:44 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:47
msgid "Query" msgid "Query"
msgstr "" msgstr ""
@ -1258,14 +1243,14 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14 #: lib/block_scout_web/templates/address_logs/index.html.eex:14
#: lib/block_scout_web/templates/layout/_topnav.html.eex:145 #: lib/block_scout_web/templates/layout/_topnav.html.eex:164
#: lib/block_scout_web/templates/layout/_topnav.html.eex:162 #: lib/block_scout_web/templates/layout/_topnav.html.eex:181
msgid "Search" msgid "Search"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:139 #: lib/block_scout_web/templates/layout/_topnav.html.eex:158
#: lib/block_scout_web/templates/layout/_topnav.html.eex:143 #: lib/block_scout_web/templates/layout/_topnav.html.eex:162
msgid "Search by address, token symbol name, transaction hash, or block number" msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr "" msgstr ""
@ -1455,8 +1440,8 @@ msgid "Topic"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:145 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:70
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:149 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:73
msgid "Topics" msgid "Topics"
msgstr "" msgstr ""
@ -1513,11 +1498,8 @@ msgid "Twitter"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:54 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:5
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:57
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:110
msgid "Type" msgid "Type"
msgstr "" msgstr ""
@ -1649,7 +1631,7 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:74 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:77
msgid "WEI" msgid "WEI"
msgstr "" msgstr ""
@ -1776,7 +1758,7 @@ msgid "Loading chart"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:189 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:113
msgid "Log Index" msgid "Log Index"
msgstr "" msgstr ""
@ -1937,7 +1919,7 @@ msgid "Waiting for transaction's confirmation..."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:44 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:47
msgid "Write" msgid "Write"
msgstr "" msgstr ""
@ -1946,3 +1928,13 @@ msgstr ""
#: lib/block_scout_web/views/address_view.ex:350 #: lib/block_scout_web/views/address_view.ex:350
msgid "Write Proxy" msgid "Write Proxy"
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:112
msgid "Apps"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:15
msgid "Copy Raw Trace"
msgstr ""

@ -1419,6 +1419,332 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
end end
end end
describe "pendingtxlist" do
test "with missing address hash", %{conn: conn} do
params = %{
"module" => "account",
"action" => "pendingtxlist"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] =~ "'address' is required"
assert response["status"] == "0"
assert Map.has_key?(response, "result")
refute response["result"]
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "with an invalid address hash", %{conn: conn} do
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "badhash"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] =~ "Invalid address format"
assert response["status"] == "0"
assert Map.has_key?(response, "result")
refute response["result"]
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "with an address that doesn't exist", %{conn: conn} do
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == []
assert response["status"] == "0"
assert response["message"] == "No transactions found"
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "with a valid address", %{conn: conn} do
address = insert(:address)
transaction =
:transaction
|> insert(from_address: address)
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "#{address.hash}"
}
expected_result = [
%{
"hash" => "#{transaction.hash}",
"nonce" => "#{transaction.nonce}",
"from" => "#{transaction.from_address_hash}",
"to" => "#{transaction.to_address_hash}",
"value" => "#{transaction.value.value}",
"gas" => "#{transaction.gas}",
"gasPrice" => "#{transaction.gas_price.value}",
"input" => "#{transaction.input}",
"contractAddress" => "#{transaction.created_contract_address_hash}",
"cumulativeGasUsed" => "#{transaction.cumulative_gas_used}",
"gasUsed" => "#{transaction.gas_used}"
}
]
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == expected_result
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "with address with multiple transactions", %{conn: conn} do
address1 = insert(:address)
address2 = insert(:address)
transactions =
3
|> insert_list(:transaction, from_address: address1)
:transaction
|> insert(from_address: address2)
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "#{address1.hash}"
}
expected_transaction_hashes = Enum.map(transactions, &"#{&1.hash}")
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert length(response["result"]) == 3
for returned_transaction <- response["result"] do
assert returned_transaction["hash"] in expected_transaction_hashes
end
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "with valid pagination params", %{conn: conn} do
address = insert(:address)
_transactions_1 =
2
|> insert_list(:transaction, from_address: address)
_transactions_2 =
2
|> insert_list(:transaction, from_address: address)
transactions_3 =
2
|> insert_list(:transaction, from_address: address)
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "#{address.hash}",
# page number
"page" => "1",
# page size
"offset" => "2"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
page1_hashes = Enum.map(response["result"], & &1["hash"])
assert length(response["result"]) == 2
for transaction <- transactions_3 do
assert "#{transaction.hash}" in page1_hashes
end
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "ignores pagination params when invalid", %{conn: conn} do
address = insert(:address)
_transactions_1 =
2
|> insert_list(:transaction, from_address: address)
_transactions_2 =
2
|> insert_list(:transaction, from_address: address)
_transactions_3 =
2
|> insert_list(:transaction, from_address: address)
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "#{address.hash}",
# page number
"page" => "invalidpage",
# page size
"offset" => "invalidoffset"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert length(response["result"]) == 6
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "ignores offset param if offset is less than 1", %{conn: conn} do
address = insert(:address)
6
|> insert_list(:transaction, from_address: address)
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "#{address.hash}",
# page number
"page" => "1",
# page size
"offset" => "0"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert length(response["result"]) == 6
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "ignores offset param if offset is over 10,000", %{conn: conn} do
address = insert(:address)
6
|> insert_list(:transaction, from_address: address)
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "#{address.hash}",
# page number
"page" => "1",
# page size
"offset" => "10_500"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert length(response["result"]) == 6
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "with page number with no results", %{conn: conn} do
address = insert(:address)
_transactions_1 =
2
|> insert_list(:transaction, from_address: address)
_transactions_2 =
2
|> insert_list(:transaction, from_address: address)
_transactions_3 =
2
|> insert_list(:transaction, from_address: address)
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "#{address.hash}",
# page number
"page" => "5",
# page size
"offset" => "2"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == []
assert response["status"] == "0"
assert response["message"] == "No transactions found"
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end
test "supports GET and POST requests", %{conn: conn} do
address = insert(:address)
:transaction
|> insert(from_address: address)
params = %{
"module" => "account",
"action" => "pendingtxlist",
"address" => "#{address.hash}"
}
assert get_response =
conn
|> get("/api", params)
|> json_response(200)
assert post_response =
conn
|> post("/api", params)
|> json_response(200)
assert get_response == post_response
end
end
describe "txlistinternal" do describe "txlistinternal" do
test "with missing txhash and address", %{conn: conn} do test "with missing txhash and address", %{conn: conn} do
params = %{ params = %{

@ -2450,7 +2450,7 @@ defmodule Explorer.Chain do
|> Repo.all() |> Repo.all()
end end
defp pending_transactions_query(query) do def pending_transactions_query(query) do
from(transaction in query, from(transaction in query,
where: is_nil(transaction.block_hash) and (is_nil(transaction.error) or transaction.error != "dropped/replaced") where: is_nil(transaction.block_hash) and (is_nil(transaction.error) or transaction.error != "dropped/replaced")
) )

@ -119,25 +119,35 @@ defmodule Explorer.Chain.Log do
@doc """ @doc """
Decode transaction log data. Decode transaction log data.
""" """
def decode(_log, %Transaction{to_address: nil}), do: {:error, :no_to_address}
def decode(log, transaction = %Transaction{to_address: %{smart_contract: %{abi: abi, address_hash: address_hash}}}) def decode(log, transaction) do
when not is_nil(abi) do address_options = [
full_abi = Chain.combine_proxy_implementation_abi(address_hash, abi) necessity_by_association: %{
:smart_contract => :optional
}
]
case Chain.find_contract_address(log.address_hash, address_options, true) do
{:ok, %{smart_contract: %{abi: abi}}} ->
full_abi = Chain.combine_proxy_implementation_abi(log.address_hash, abi)
with {:ok, selector, mapping} <- find_and_decode(full_abi, log, transaction),
identifier <- Base.encode16(selector.method_id, case: :lower),
text <- function_call(selector.function, mapping),
do: {:ok, identifier, text, mapping}
with {:ok, selector, mapping} <- find_and_decode(full_abi, log, transaction), _ ->
identifier <- Base.encode16(selector.method_id, case: :lower), find_candidates(log, transaction)
text <- function_call(selector.function, mapping), end
do: {:ok, identifier, text, mapping}
end end
def decode(log, transaction) do defp find_candidates(log, transaction) do
case log.first_topic do case log.first_topic do
"0x" <> hex_part -> "0x" <> hex_part ->
case Integer.parse(hex_part, 16) do case Integer.parse(hex_part, 16) do
{number, ""} -> {number, ""} ->
<<method_id::binary-size(4), _rest::binary>> = :binary.encode_unsigned(number) <<method_id::binary-size(4), _rest::binary>> = :binary.encode_unsigned(number)
find_candidates(method_id, log, transaction) find_candidates_query(method_id, log, transaction)
_ -> _ ->
{:error, :could_not_decode} {:error, :could_not_decode}
@ -148,7 +158,7 @@ defmodule Explorer.Chain.Log do
end end
end end
defp find_candidates(method_id, log, transaction) do defp find_candidates_query(method_id, log, transaction) do
candidates_query = candidates_query =
from( from(
contract_method in ContractMethod, contract_method in ContractMethod,

@ -255,20 +255,26 @@ defmodule Explorer.Chain.SmartContract do
|> prepare_changes(&upsert_contract_methods/1) |> prepare_changes(&upsert_contract_methods/1)
end end
def invalid_contract_changeset(%__MODULE__{} = smart_contract, attrs, error) do def invalid_contract_changeset(%__MODULE__{} = smart_contract, attrs, error, error_message) do
smart_contract validated =
|> cast(attrs, [ smart_contract
:name, |> cast(attrs, [
:compiler_version, :name,
:optimization, :compiler_version,
:contract_source_code, :optimization,
:address_hash, :contract_source_code,
:evm_version, :address_hash,
:optimization_runs, :evm_version,
:constructor_arguments :optimization_runs,
]) :constructor_arguments
|> validate_required([:name, :compiler_version, :optimization, :address_hash]) ])
|> add_error(:contract_source_code, error_message(error)) |> validate_required([:name, :compiler_version, :optimization, :address_hash])
if error_message do
add_error(validated, :contract_source_code, error_message(error, error_message))
else
add_error(validated, :contract_source_code, error_message(error))
end
end end
def add_submitted_comment(code, inserted_at) when is_binary(code) do def add_submitted_comment(code, inserted_at) when is_binary(code) do
@ -331,4 +337,5 @@ defmodule Explorer.Chain.SmartContract do
defp error_message(:constructor_arguments), do: "Constructor arguments do not match, please try again." defp error_message(:constructor_arguments), do: "Constructor arguments do not match, please try again."
defp error_message(:name), do: "Wrong contract name, please try again." defp error_message(:name), do: "Wrong contract name, please try again."
defp error_message(_), do: "There was an error validating your contract, please try again." defp error_message(_), do: "There was an error validating your contract, please try again."
defp error_message(:compilation, error_message), do: "There was an error compiling your contract: #{error_message}"
end end

@ -3,7 +3,7 @@ defmodule Explorer.Etherscan do
The etherscan context. The etherscan context.
""" """
import Ecto.Query, only: [from: 2, where: 3, or_where: 3, union: 2, subquery: 1] import Ecto.Query, only: [from: 2, where: 3, or_where: 3, union: 2, subquery: 1, order_by: 3]
alias Explorer.Etherscan.Logs alias Explorer.Etherscan.Logs
alias Explorer.{Chain, Repo} alias Explorer.{Chain, Repo}
@ -52,6 +52,22 @@ defmodule Explorer.Etherscan do
end end
end end
@doc """
Gets a list of pending transactions for a given `t:Explorer.Chain.Hash.Address.t/0`.
If `filter_by: `to_address_hash`,
`from_address_hash`, and `created_contract_address_hash`.
"""
@spec list_pending_transactions(Hash.Address.t()) :: [map()]
def list_pending_transactions(
%Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash,
options \\ @default_options
) do
merged_options = Map.merge(@default_options, options)
list_pending_transactions_query(address_hash, merged_options)
end
@internal_transaction_fields ~w( @internal_transaction_fields ~w(
from_address_hash from_address_hash
to_address_hash to_address_hash
@ -335,6 +351,38 @@ defmodule Explorer.Etherscan do
revert_reason revert_reason
)a )a
@pending_transaction_fields ~w(
created_contract_address_hash
cumulative_gas_used
from_address_hash
gas
gas_price
gas_used
hash
index
input
nonce
to_address_hash
value
inserted_at
)a
defp list_pending_transactions_query(address_hash, options) do
query =
from(
t in Transaction,
limit: ^options.page_size,
offset: ^offset(options),
select: map(t, ^@pending_transaction_fields)
)
query
|> where_address_match(address_hash, options)
|> Chain.pending_transactions_query()
|> order_by([transaction], desc: transaction.inserted_at, desc: transaction.hash)
|> Repo.all()
end
defp list_transactions(address_hash, max_block_number, options) do defp list_transactions(address_hash, max_block_number, options) do
query = query =
from( from(

@ -37,7 +37,10 @@ defmodule Explorer.SmartContract.Publisher do
publish_smart_contract(address_hash, params_with_external_libaries, abi) publish_smart_contract(address_hash, params_with_external_libaries, abi)
{:error, error} -> {:error, error} ->
{:error, unverified_smart_contract(address_hash, params_with_external_libaries, error)} {:error, unverified_smart_contract(address_hash, params_with_external_libaries, error, nil)}
{:error, error, error_message} ->
{:error, unverified_smart_contract(address_hash, params_with_external_libaries, error, error_message)}
end end
end end
@ -47,14 +50,15 @@ defmodule Explorer.SmartContract.Publisher do
Chain.create_smart_contract(attrs, attrs.external_libraries) Chain.create_smart_contract(attrs, attrs.external_libraries)
end end
defp unverified_smart_contract(address_hash, params, error) do defp unverified_smart_contract(address_hash, params, error, error_message) do
attrs = attributes(address_hash, params) attrs = attributes(address_hash, params)
changeset = changeset =
SmartContract.invalid_contract_changeset( SmartContract.invalid_contract_changeset(
%SmartContract{address_hash: address_hash}, %SmartContract{address_hash: address_hash},
attrs, attrs,
error error,
error_message
) )
%{changeset | action: :insert} %{changeset | action: :insert}

@ -116,7 +116,9 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
error -> error ->
error = parse_error(error) error = parse_error(error)
Logger.warn(["There was an error compiling a provided contract: ", inspect(error)]) Logger.warn(["There was an error compiling a provided contract: ", inspect(error)])
{:error, :compilation} {:error, [first_error | _]} = error
%{"message" => error_message} = first_error
{:error, :compilation, error_message}
end end
else else
{:error, :compilation} {:error, :compilation}

@ -28,7 +28,11 @@ defmodule Explorer.SmartContract.Verifier do
latest_evm_version = List.last(CodeCompiler.allowed_evm_versions()) latest_evm_version = List.last(CodeCompiler.allowed_evm_versions())
evm_version = Map.get(params, "evm_version", latest_evm_version) evm_version = Map.get(params, "evm_version", latest_evm_version)
Enum.reduce([evm_version | previous_evm_versions(evm_version)], false, fn version, acc -> all_versions = [evm_version | previous_evm_versions(evm_version)]
all_versions_extra = all_versions ++ [evm_version]
Enum.reduce(all_versions_extra, false, fn version, acc ->
case acc do case acc do
{:ok, _} = result -> {:ok, _} = result ->
result result
@ -75,6 +79,10 @@ defmodule Explorer.SmartContract.Verifier do
defp compare_bytecodes({:error, :name}, _, _, _, _, _), do: {:error, :name} defp compare_bytecodes({:error, :name}, _, _, _, _, _), do: {:error, :name}
defp compare_bytecodes({:error, _}, _, _, _, _, _), do: {:error, :compilation} defp compare_bytecodes({:error, _}, _, _, _, _, _), do: {:error, :compilation}
defp compare_bytecodes({:error, _, error_message}, _, _, _, _, _) do
{:error, :compilation, error_message}
end
# credo:disable-for-next-line /Complexity/ # credo:disable-for-next-line /Complexity/
defp compare_bytecodes( defp compare_bytecodes(
{:ok, %{"abi" => abi, "bytecode" => bytecode}}, {:ok, %{"abi" => abi, "bytecode" => bytecode}},

@ -58,29 +58,29 @@ defmodule Explorer.Chain.LogTest do
end end
test "that a contract call transaction that has a verified contract returns the decoded input data" do test "that a contract call transaction that has a verified contract returns the decoded input data" do
smart_contract = to_address = insert(:address, contract_code: "0x")
insert(:smart_contract,
abi: [ insert(:smart_contract,
%{ abi: [
"anonymous" => false, %{
"inputs" => [ "anonymous" => false,
%{"indexed" => true, "name" => "_from_human", "type" => "string"}, "inputs" => [
%{"indexed" => false, "name" => "_number", "type" => "uint256"}, %{"indexed" => true, "name" => "_from_human", "type" => "string"},
%{"indexed" => true, "name" => "_belly", "type" => "bool"} %{"indexed" => false, "name" => "_number", "type" => "uint256"},
], %{"indexed" => true, "name" => "_belly", "type" => "bool"}
"name" => "WantsPets", ],
"type" => "event" "name" => "WantsPets",
} "type" => "event"
] }
) ],
address_hash: to_address.hash
)
topic1 = "0x" <> Base.encode16(:keccakf1600.hash(:sha3_256, "WantsPets(string,uint256,bool)"), case: :lower) topic1 = "0x" <> Base.encode16(:keccakf1600.hash(:sha3_256, "WantsPets(string,uint256,bool)"), case: :lower)
topic2 = "0x" <> Base.encode16(:keccakf1600.hash(:sha3_256, "bob"), case: :lower) topic2 = "0x" <> Base.encode16(:keccakf1600.hash(:sha3_256, "bob"), case: :lower)
topic3 = "0x0000000000000000000000000000000000000000000000000000000000000001" topic3 = "0x0000000000000000000000000000000000000000000000000000000000000001"
data = "0x0000000000000000000000000000000000000000000000000000000000000000" data = "0x0000000000000000000000000000000000000000000000000000000000000000"
to_address = insert(:address, smart_contract: smart_contract)
transaction = transaction =
:transaction_to_verified_contract :transaction_to_verified_contract
|> insert(to_address: to_address) |> insert(to_address: to_address)
@ -88,6 +88,7 @@ defmodule Explorer.Chain.LogTest do
log = log =
insert(:log, insert(:log,
address: to_address,
transaction: transaction, transaction: transaction,
first_topic: topic1, first_topic: topic1,
second_topic: topic2, second_topic: topic2,

@ -463,6 +463,156 @@ defmodule Explorer.EtherscanTest do
end end
end end
describe "list_pending_transactions/2" do
test "with empty db" do
address = build(:address)
assert Etherscan.list_pending_transactions(address.hash) == []
end
test "with from address" do
address = insert(:address)
transaction =
:transaction
|> insert(from_address: address)
[found_transaction] = Etherscan.list_pending_transactions(address.hash)
assert transaction.hash == found_transaction.hash
end
test "with to address" do
address = insert(:address)
transaction =
:transaction
|> insert(to_address: address)
[found_transaction] = Etherscan.list_pending_transactions(address.hash)
assert transaction.hash == found_transaction.hash
end
test "with same to and from address" do
address = insert(:address)
_transaction =
:transaction
|> insert(from_address: address, to_address: address)
found_transactions = Etherscan.list_pending_transactions(address.hash)
assert length(found_transactions) == 1
end
test "with address with 0 transactions" do
address1 = insert(:address)
address2 = insert(:address)
:transaction
|> insert(from_address: address2)
assert Etherscan.list_pending_transactions(address1.hash) == []
end
test "with address with multiple transactions" do
address1 = insert(:address)
address2 = insert(:address)
3
|> insert_list(:transaction, from_address: address1)
:transaction
|> insert(from_address: address2)
found_transactions = Etherscan.list_pending_transactions(address1.hash)
assert length(found_transactions) == 3
for found_transaction <- found_transactions do
assert found_transaction.from_address_hash == address1.hash
end
end
test "orders transactions by inserted_at, in descending order" do
address = insert(:address)
2
|> insert_list(:transaction, from_address: address)
2
|> insert_list(:transaction, from_address: address)
2
|> insert_list(:transaction, from_address: address)
options = %{order_by_direction: :desc}
found_transactions = Etherscan.list_pending_transactions(address.hash, options)
inserted_at_order = Enum.map(found_transactions, & &1.inserted_at)
assert inserted_at_order == Enum.sort(inserted_at_order, &(&1 >= &2))
end
test "with page_size and page_number options" do
address = insert(:address)
transactions_1 =
2
|> insert_list(:transaction, from_address: address)
transactions_2 =
2
|> insert_list(:transaction, from_address: address)
transactions_3 =
2
|> insert_list(:transaction, from_address: address)
options = %{page_number: 1, page_size: 2}
page1_transactions = Etherscan.list_pending_transactions(address.hash, options)
page1_hashes = Enum.map(page1_transactions, & &1.hash)
assert length(page1_transactions) == 2
for transaction <- transactions_3 do
assert transaction.hash in page1_hashes
end
options = %{page_number: 2, page_size: 2}
page2_transactions = Etherscan.list_pending_transactions(address.hash, options)
page2_hashes = Enum.map(page2_transactions, & &1.hash)
assert length(page2_transactions) == 2
for transaction <- transactions_2 do
assert transaction.hash in page2_hashes
end
options = %{page_number: 3, page_size: 2}
page3_transactions = Etherscan.list_pending_transactions(address.hash, options)
page3_hashes = Enum.map(page3_transactions, & &1.hash)
assert length(page3_transactions) == 2
for transaction <- transactions_1 do
assert transaction.hash in page3_hashes
end
options = %{page_number: 4, page_size: 2}
assert Etherscan.list_pending_transactions(address.hash, options) == []
end
end
describe "list_internal_transactions/1 with transaction hash" do describe "list_internal_transactions/1 with transaction hash" do
test "with empty db" do test "with empty db" do
transaction = build(:transaction) transaction = build(:transaction)

@ -220,6 +220,12 @@ endif
ifdef TXS_STATS_DAYS_TO_COMPILE_AT_INIT ifdef TXS_STATS_DAYS_TO_COMPILE_AT_INIT
BLOCKSCOUT_CONTAINER_PARAMS += -e 'TXS_STATS_DAYS_TO_COMPILE_AT_INIT=$(TXS_STATS_DAYS_TO_COMPILE_AT_INIT)' BLOCKSCOUT_CONTAINER_PARAMS += -e 'TXS_STATS_DAYS_TO_COMPILE_AT_INIT=$(TXS_STATS_DAYS_TO_COMPILE_AT_INIT)'
endif endif
ifdef APPS_MENU
BLOCKSCOUT_CONTAINER_PARAMS += -e 'APPS_MENU=$(APPS_MENU)'
endif
ifdef EXTERNAL_APPS
BLOCKSCOUT_CONTAINER_PARAMS += -e 'EXTERNAL_APPS=$(EXTERNAL_APPS)'
endif
HAS_BLOCKSCOUT_IMAGE := $(shell docker images | grep ${DOCKER_IMAGE}) HAS_BLOCKSCOUT_IMAGE := $(shell docker images | grep ${DOCKER_IMAGE})
build: build:

Loading…
Cancel
Save