API and smart-contracts fixes (#7614)

* Add icon url to search results

* Move vyper evm versions to envs

* Fix smart contracts reading

* Fix review comments
pull/7636/head
nikitosing 1 year ago committed by GitHub
parent dac2e570b1
commit e81f2ee75a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 2
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
  3. 2
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex
  4. 2
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_multi_part_files_controller.ex
  5. 1
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex
  6. 5
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex
  7. 3
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex
  8. 2
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  9. 2
      apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
  10. 3
      apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex
  11. 38
      apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex
  12. 44
      apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex
  13. 284
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs
  14. 18
      apps/explorer/lib/explorer/chain.ex
  15. 3
      apps/explorer/lib/explorer/smart_contract/helper.ex
  16. 49
      apps/explorer/lib/explorer/smart_contract/reader.ex
  17. 18
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  18. 10
      apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
  19. 8
      apps/explorer/test/explorer/smart_contract/reader_test.exs
  20. 14
      apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs
  21. 9
      config/runtime.exs
  22. 3
      docker-compose/envs/common-blockscout.env
  23. 7
      docker/Makefile

@ -14,8 +14,9 @@
- [#7635](https://github.com/blockscout/blockscout/pull/7635) - Fix single 1155 transfer displaying
- [#7629](https://github.com/blockscout/blockscout/pull/7629) - Fix NFT fetcher
- [#7614](https://github.com/blockscout/blockscout/pull/7614) - API and smart-contracts fixes and improvements
- [#7611](https://github.com/blockscout/blockscout/pull/7611) - Fix tokens pagination
- [#7566](https://github.com/blockscout/blockscout/pull/7566) - Account: check composed email beofre sending
- [#7566](https://github.com/blockscout/blockscout/pull/7566) - Account: check composed email before sending
- [#7564](https://github.com/blockscout/blockscout/pull/7564) - Return contract type in address view
- [#7562](https://github.com/blockscout/blockscout/pull/7562) - Remove fallback from Read methods
- [#7537](https://github.com/blockscout/blockscout/pull/7537), [#7553](https://github.com/blockscout/blockscout/pull/7553) - Withdrawals fixes and improvements

@ -38,7 +38,7 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
render(conn, "new.html",
changeset: changeset,
compiler_versions: compiler_versions,
evm_versions: CodeCompiler.allowed_evm_versions(),
evm_versions: CodeCompiler.evm_versions(:solidity),
address_hash: address_hash_string
)
end

@ -33,7 +33,7 @@ defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController do
render(conn, "new.html",
changeset: changeset,
compiler_versions: compiler_versions,
evm_versions: CodeCompiler.allowed_evm_versions(),
evm_versions: CodeCompiler.evm_versions(:solidity),
address_hash: address_hash_string
)
end

@ -33,7 +33,7 @@ defmodule BlockScoutWeb.AddressContractVerificationViaMultiPartFilesController d
render(conn, "new.html",
changeset: changeset,
address_hash: address_hash_string,
evm_versions: CodeCompiler.allowed_evm_versions(),
evm_versions: CodeCompiler.evm_versions(:solidity),
compiler_versions: compiler_versions
)
end

@ -176,6 +176,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do
contract_type,
params["from"],
address.smart_contract.abi,
true,
@api_true
)
end

@ -16,7 +16,6 @@ defmodule BlockScoutWeb.API.V2.VerificationController do
@api_true [api?: true]
def config(conn, _params) do
evm_versions = CodeCompiler.allowed_evm_versions()
solidity_compiler_versions = CompilerVersion.fetch_version_list(:solc)
vyper_compiler_versions = CompilerVersion.fetch_version_list(:vyper)
@ -31,11 +30,11 @@ defmodule BlockScoutWeb.API.V2.VerificationController do
conn
|> json(%{
solidity_evm_versions: evm_versions,
solidity_evm_versions: CodeCompiler.evm_versions(:solidity),
solidity_compiler_versions: solidity_compiler_versions,
vyper_compiler_versions: vyper_compiler_versions,
verification_options: verification_options,
vyper_evm_versions: ["byzantium", "constantinople", "petersburg", "istanbul"],
vyper_evm_versions: CodeCompiler.evm_versions(:vyper),
is_rust_verifier_microservice_enabled: RustVerifierInterface.enabled?()
})
end

@ -196,7 +196,8 @@ defmodule BlockScoutWeb.SmartContractController do
%{method_id: params["method_id"], args: args},
contract_type,
params["from"],
address.smart_contract && address.smart_contract.abi
address.smart_contract && address.smart_contract.abi,
true
)
end

@ -80,7 +80,7 @@ defmodule BlockScoutWeb.Notifier do
|> View.render_to_string("new.html",
changeset: changeset,
compiler_versions: compiler_versions,
evm_versions: CodeCompiler.allowed_evm_versions(),
evm_versions: CodeCompiler.evm_versions(:solidity),
address_hash: address_hash,
conn: conn,
retrying: true

@ -145,7 +145,7 @@
</div>
</div>
<% else %>
<div class="align-self-center function-output word-break-all <%= if not_last_element?(length, index), do: "mb-1" %>"><%= raw(values_with_type(output["value"], output["type"], [output["name"]], 0, output["components"])) %></div>
<div class="align-self-center function-output word-break-all <%= if not_last_element?(length, index), do: "mb-1" %>"><%= raw(values_with_type(output["value"], output["type"], fetch_name(function["names"], index), 0)) %></div>
<% end %>
<% end %>
<% end %>

@ -23,7 +23,8 @@ defmodule BlockScoutWeb.API.V2.SearchView do
"symbol" => search_result.symbol,
"address" => search_result.address_hash,
"token_url" => token_path(Endpoint, :show, search_result.address_hash),
"address_url" => address_path(Endpoint, :show, search_result.address_hash)
"address_url" => address_path(Endpoint, :show, search_result.address_hash),
"icon_url" => search_result.icon_url
}
end

@ -98,7 +98,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
%{result: %{error: error}, is_error: true}
_ ->
%{result: %{output: outputs, names: names}, is_error: false}
%{result: %{output: Enum.map(outputs, &render_json/1), names: names}, is_error: false}
end
end
@ -118,13 +118,13 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
function
|> Map.drop(["abi_outputs"])
outputs = Enum.map(result["outputs"], &prepare_output/1)
outputs = result["outputs"] |> Enum.map(&prepare_output/1)
Map.replace(result, "outputs", outputs)
end
end
defp prepare_output(%{"type" => type, "value" => value} = output) do
Map.replace(output, "value", ABIEncodedValueView.value_json(type, value))
Map.replace(output, "value", render_json(value, type))
end
defp prepare_output(output), do: output
@ -277,4 +277,36 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
"solidity"
end
end
def render_json(%{"type" => type, "value" => value}) do
%{"type" => type, "value" => render_json(value, type)}
end
def render_json(value, type) when type in [:address, "address", "address payable"] do
SmartContractView.cast_address(value)
end
def render_json(value, type) when type in [:string, "string"] do
to_string(value)
end
def render_json(value, type) when is_tuple(value) do
value
|> SmartContractView.zip_tuple_values_with_types(type)
|> Enum.map(fn {type, value} ->
render_json(value, type)
end)
end
def render_json(value, type) when is_list(value) do
value |> Enum.map(&render_json(&1, type))
end
def render_json(value, _type) when is_binary(value) do
SmartContractView.binary_to_utf_string(value)
end
def render_json(value, _type) do
value
end
end

@ -6,6 +6,8 @@ defmodule BlockScoutWeb.SmartContractView do
alias Explorer.Chain.Hash.Address, as: HashAddress
alias Explorer.SmartContract.Helper
require Logger
def queryable?(inputs) when not is_nil(inputs), do: Enum.any?(inputs)
def queryable?(inputs) when is_nil(inputs), do: false
@ -63,7 +65,7 @@ defmodule BlockScoutWeb.SmartContractView do
String.starts_with?(type, "address") ->
values =
value
|> Enum.map_join(", ", &binary_to_utf_string(&1))
|> Enum.map_join(", ", &cast_address(&1))
render_array_type_value(type, values, fetch_name(names, index))
@ -117,6 +119,17 @@ defmodule BlockScoutWeb.SmartContractView do
def values_with_type(value, :error, _components),
do: render_type_value("error", Helper.sanitize_input(value), "error")
def cast_address(value) do
case HashAddress.cast(value) do
{:ok, address} ->
to_string(address)
_ ->
Logger.warn(fn -> ["Error decoding address value: #{inspect(value)}"] end)
"(decoding error)"
end
end
defp fetch_name(nil, _index), do: nil
defp fetch_name([], _index), do: nil
@ -137,6 +150,15 @@ defmodule BlockScoutWeb.SmartContractView do
end
defp tuple_to_array(value, type, names) do
value
|> zip_tuple_values_with_types(type)
|> Enum.with_index()
|> Enum.map(fn {{type, value}, index} ->
values_with_type(value, type, fetch_name(names, index), 0)
end)
end
def zip_tuple_values_with_types(value, type) do
types_string =
type
|> String.slice(6..-2)
@ -165,16 +187,10 @@ defmodule BlockScoutWeb.SmartContractView do
value
|> Tuple.to_list()
values_types_list = Enum.zip(tuple_types, values_list)
values_types_list
|> Enum.with_index()
|> Enum.map(fn {{type, value}, index} ->
values_with_type(value, type, fetch_name(names, index), 0)
end)
Enum.zip(tuple_types, values_list)
end
defp compose_array_if_to_merge(arr, val, to_merge) do
def compose_array_if_to_merge(arr, val, to_merge) do
if count_string_symbols(val)["]"] > count_string_symbols(val)["["] do
updated_arr = update_last_list_item(arr, val)
{updated_arr, !to_merge}
@ -184,7 +200,7 @@ defmodule BlockScoutWeb.SmartContractView do
end
end
defp compose_array_else(arr, val, to_merge) do
def compose_array_else(arr, val, to_merge) do
if count_string_symbols(val)["["] > count_string_symbols(val)["]"] do
# credo:disable-for-next-line
{arr ++ [val], !to_merge}
@ -214,7 +230,7 @@ defmodule BlockScoutWeb.SmartContractView do
end)
end
defp binary_to_utf_string(item) do
def binary_to_utf_string(item) do
case Integer.parse(to_string(item)) do
{item_integer, ""} ->
to_string(item_integer)
@ -229,12 +245,8 @@ defmodule BlockScoutWeb.SmartContractView do
end
defp add_0x(item) do
if String.starts_with?(item, "0x") do
item
else
"0x" <> Base.encode16(item, case: :lower)
end
end
defp render_type_value(type, value, type) do
"<div class=\"pl-3\"><i>(#{Helper.sanitize_input(type)})</i> : #{value}</div>"
@ -250,7 +262,7 @@ defmodule BlockScoutWeb.SmartContractView do
render_type_value(type, value_to_display, name)
end
defp supplement_type_with_components(type, components) do
def supplement_type_with_components(type, components) do
if type == "tuple" && components do
types =
components

@ -462,24 +462,83 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
"name" => "disableWhitelist",
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}]
},
%{"type" => "fallback"}
%{"type" => "fallback"},
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [
%{
"type" => "tuple",
"name" => "",
"internalType" => "struct Storage.TransactionReceipt",
"components" => [
%{"type" => "bytes32", "name" => "txHash", "internalType" => "bytes32"},
%{"type" => "uint256", "name" => "blockNumber", "internalType" => "uint256"},
%{"type" => "bytes32", "name" => "blockHash", "internalType" => "bytes32"},
%{"type" => "uint256", "name" => "transactionIndex", "internalType" => "uint256"},
%{"type" => "address", "name" => "from", "internalType" => "address"},
%{"type" => "address", "name" => "to", "internalType" => "address"},
%{"type" => "uint256", "name" => "gasUsed", "internalType" => "uint256"},
%{"type" => "bool", "name" => "status", "internalType" => "bool"},
%{
"type" => "tuple[]",
"name" => "logs",
"internalType" => "struct Storage.Log[]",
"components" => [
%{"type" => "address", "name" => "from", "internalType" => "address"},
%{"type" => "bytes32[]", "name" => "topics", "internalType" => "bytes32[]"},
%{"type" => "bytes", "name" => "data", "internalType" => "bytes"}
]
}
]
}
],
"name" => "retrieve",
"inputs" => []
}
]
target_contract = insert(:smart_contract, abi: abi)
blockchain_eth_call_mock()
request = get(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/methods-read")
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [
%{
id: id,
method: "eth_call",
params: [%{to: _address_hash, from: "0xBb36c792B9B45Aaf8b848A1392B0d6559202729E"}, _]
}
],
_opts ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result:
"0x0000000000000000000000000000000000000000000000000000000000000020fe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab200000000000000000000000000000000000000000000000000000000000003e8fe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab2000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000001e0f30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000"
}
]}
end
)
request =
get(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/methods-read", %{
"from" => "0xBb36c792B9B45Aaf8b848A1392B0d6559202729E"
})
assert response = json_response(request, 200)
assert %{
"type" => "function",
"stateMutability" => "view",
"names" => ["address"],
"outputs" => [
%{
"type" => "address",
"name" => "",
"internalType" => "address",
"value" => "0xfffffffffffffffffffffffffffffffffffffffe"
}
],
@ -497,6 +556,75 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
"method_id" => "c683630d"
} in response
assert %{
"inputs" => [],
"method_id" => "2e64cec1",
"name" => "retrieve",
"names" => [
[
"struct Storage.TransactionReceipt",
[
"txHash",
"blockNumber",
"blockHash",
"transactionIndex",
"from",
"to",
"gasUsed",
"status",
["logs", ["from", "topics", "data"]]
]
]
],
"outputs" => [
%{
"type" =>
"tuple[bytes32,uint256,bytes32,uint256,address,address,uint256,bool,tuple[address,bytes32[],bytes][]]",
"value" => [
"0xfe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab2",
1000,
"0xfe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab2",
10,
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
123_123,
true,
[
[
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
[
"0x3078300000000000000000000000000000000000000000000000000000000000",
"0x3078303031313232333300000000000000000000000000000000000000000000",
"0x3078303031313232333331323300000000000000000000000000000000000000"
],
"0x307830303030313233313233"
],
[
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
[
"0x3078300000000000000000000000000000000000000000000000000000000000",
"0x3078303031313232333300000000000000000000000000000000000000000000",
"0x3078303031313232333331323300000000000000000000000000000000000000"
],
"0x307830303030313233313233"
],
[
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
[
"0x3078300000000000000000000000000000000000000000000000000000000000",
"0x3078303031313232333300000000000000000000000000000000000000000000",
"0x3078303031313232333331323300000000000000000000000000000000000000"
],
"0x307830303030313233313233"
]
]
]
}
],
"stateMutability" => "view",
"type" => "function"
} in response
refute %{"type" => "fallback"} in response
end
@ -544,10 +672,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"names" => [nil],
"outputs" => [
%{
"type" => "address[]",
"name" => "",
"value" => [
"0x64631b5d259ead889e8b06d12c8b74742804e5f1",
"0x234fe7224ce480ca97d01897311b8c3d35162f86",
@ -666,6 +794,146 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
} == response
end
test "query complex response", %{conn: conn} do
abi = [
%{
"type" => "function",
"stateMutability" => "view",
"outputs" => [
%{
"type" => "tuple",
"name" => "",
"internalType" => "struct Storage.TransactionReceipt",
"components" => [
%{"type" => "bytes32", "name" => "txHash", "internalType" => "bytes32"},
%{"type" => "uint256", "name" => "blockNumber", "internalType" => "uint256"},
%{"type" => "bytes32", "name" => "blockHash", "internalType" => "bytes32"},
%{"type" => "uint256", "name" => "transactionIndex", "internalType" => "uint256"},
%{"type" => "address", "name" => "from", "internalType" => "address"},
%{"type" => "address", "name" => "to", "internalType" => "address"},
%{"type" => "uint256", "name" => "gasUsed", "internalType" => "uint256"},
%{"type" => "bool", "name" => "status", "internalType" => "bool"},
%{
"type" => "tuple[]",
"name" => "logs",
"internalType" => "struct Storage.Log[]",
"components" => [
%{"type" => "address", "name" => "from", "internalType" => "address"},
%{"type" => "bytes32[]", "name" => "topics", "internalType" => "bytes32[]"},
%{"type" => "bytes", "name" => "data", "internalType" => "bytes"}
]
}
]
}
],
"name" => "retrieve",
"inputs" => []
}
]
target_contract = insert(:smart_contract, abi: abi)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [
%{
id: id,
method: "eth_call",
params: [%{to: _address_hash, from: "0xBb36c792B9B45Aaf8b848A1392B0d6559202729E"}, _]
}
],
_opts ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result:
"0x0000000000000000000000000000000000000000000000000000000000000020fe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab200000000000000000000000000000000000000000000000000000000000003e8fe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab2000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000001e0f30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000"
}
]}
end
)
request =
post(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/query-read-method", %{
"contract_type" => "regular",
"args" => [],
"method_id" => "2e64cec1",
"from" => "0xBb36c792B9B45Aaf8b848A1392B0d6559202729E"
})
assert response = json_response(request, 200)
assert %{
"is_error" => false,
"result" => %{
"names" => [
[
"struct Storage.TransactionReceipt",
[
"txHash",
"blockNumber",
"blockHash",
"transactionIndex",
"from",
"to",
"gasUsed",
"status",
["logs", ["from", "topics", "data"]]
]
]
],
"output" => [
%{
"type" =>
"tuple[bytes32,uint256,bytes32,uint256,address,address,uint256,bool,tuple[address,bytes32[],bytes][]]",
"value" => [
"0xfe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab2",
1000,
"0xfe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab2",
10,
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
123_123,
true,
[
[
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
[
"0x3078300000000000000000000000000000000000000000000000000000000000",
"0x3078303031313232333300000000000000000000000000000000000000000000",
"0x3078303031313232333331323300000000000000000000000000000000000000"
],
"0x307830303030313233313233"
],
[
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
[
"0x3078300000000000000000000000000000000000000000000000000000000000",
"0x3078303031313232333300000000000000000000000000000000000000000000",
"0x3078303031313232333331323300000000000000000000000000000000000000"
],
"0x307830303030313233313233"
],
[
"0xbb36c792b9b45aaf8b848a1392b0d6559202729e",
[
"0x3078300000000000000000000000000000000000000000000000000000000000",
"0x3078303031313232333300000000000000000000000000000000000000000000",
"0x3078303031313232333331323300000000000000000000000000000000000000"
],
"0x307830303030313233313233"
]
]
]
}
]
}
} == response
end
test "query-read-method with nonexistent method_id", %{conn: conn} do
abi = [
%{
@ -1041,11 +1309,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert %{
"type" => "function",
"stateMutability" => "view",
"names" => ["address"],
"outputs" => [
%{
"type" => "address",
"name" => "",
"internalType" => "address",
"value" => "0xfffffffffffffffffffffffffffffffffffffffe"
}
],
@ -1220,11 +1487,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert %{
"type" => "function",
"stateMutability" => "view",
"names" => ["address"],
"outputs" => [
%{
"type" => "address",
"name" => "",
"internalType" => "address",
"value" => "0xfffffffffffffffffffffffffffffffffffffffe"
}
],

@ -1512,7 +1512,8 @@ defmodule Explorer.Chain do
symbol: token.symbol,
holder_count: token.holder_count,
inserted_at: token.inserted_at,
block_number: 0
block_number: 0,
icon_url: token.icon_url
}
)
end
@ -1531,7 +1532,8 @@ defmodule Explorer.Chain do
symbol: ^nil,
holder_count: ^nil,
inserted_at: address.inserted_at,
block_number: 0
block_number: 0,
icon_url: nil
}
)
end
@ -1559,7 +1561,8 @@ defmodule Explorer.Chain do
symbol: ^nil,
holder_count: ^nil,
inserted_at: address.inserted_at,
block_number: 0
block_number: 0,
icon_url: nil
}
)
@ -1582,7 +1585,8 @@ defmodule Explorer.Chain do
symbol: ^nil,
holder_count: ^nil,
inserted_at: transaction.inserted_at,
block_number: 0
block_number: 0,
icon_url: nil
}
)
@ -1605,7 +1609,8 @@ defmodule Explorer.Chain do
symbol: ^nil,
holder_count: ^nil,
inserted_at: block.inserted_at,
block_number: block.number
block_number: block.number,
icon_url: nil
}
)
@ -1623,7 +1628,8 @@ defmodule Explorer.Chain do
symbol: ^nil,
holder_count: ^nil,
inserted_at: block.inserted_at,
block_number: block.number
block_number: block.number,
icon_url: nil
}
)

@ -24,7 +24,8 @@ defmodule Explorer.SmartContract.Helper do
@spec read_with_wallet_method?(%{}) :: true | false
def read_with_wallet_method?(function),
do:
!error?(function) && !event?(function) && !constructor?(function) && !fallback?(function) && nonpayable?(function) &&
!error?(function) && !event?(function) && !constructor?(function) && !fallback?(function) &&
nonpayable?(function) &&
!empty_outputs?(function)
def empty_inputs?(function), do: function["inputs"] == []

@ -270,7 +270,7 @@ defmodule Explorer.SmartContract.Reader do
abi_with_method_id
|> Enum.filter(&Helper.queriable_method?(&1))
|> Enum.map(&fetch_current_value_from_blockchain(&1, abi_with_method_id, contract_address_hash, false, from))
|> Enum.map(&fetch_current_value_from_blockchain(&1, abi_with_method_id, contract_address_hash, false, [], from))
end
def read_only_functions_from_abi_with_sender(_, _, _), do: []
@ -332,21 +332,34 @@ defmodule Explorer.SmartContract.Reader do
"tuple[#{tuple_types}]"
end
def fetch_current_value_from_blockchain(function, abi, contract_address_hash, leave_error_as_map, from \\ nil) do
def fetch_current_value_from_blockchain(
function,
abi,
contract_address_hash,
leave_error_as_map,
options,
from \\ nil
) do
case function do
%{"inputs" => []} ->
method_id = function["method_id"]
args = function["inputs"]
outputs = function["outputs"]
values =
contract_address_hash
|> query_verified_contract(%{method_id => normalize_args(args)}, from, leave_error_as_map, abi)
|> link_outputs_and_values(outputs, method_id)
%{output: outputs, names: names} =
query_function_with_names(
contract_address_hash,
%{method_id: method_id, args: args},
:regular,
from,
abi,
leave_error_as_map,
options
)
function
|> Map.replace!("outputs", values)
|> Map.replace!("outputs", outputs)
|> Map.put("abi_outputs", Map.get(function, "outputs", []))
|> Map.put("names", names)
_ ->
function
@ -364,9 +377,10 @@ defmodule Explorer.SmartContract.Reader do
%{method_id: String.t(), args: [term()] | nil},
:regular | :proxy,
String.t() | nil,
[api?]
[],
boolean()
) :: %{:names => [any()], :output => [%{}]}
def query_function_with_names(contract_address_hash, params, type, from, abi, options \\ [])
def query_function_with_names(contract_address_hash, params, type, from, abi, leave_error_as_map, options \\ [])
def query_function_with_names(
contract_address_hash,
@ -374,6 +388,7 @@ defmodule Explorer.SmartContract.Reader do
:regular,
from,
abi,
leave_error_as_map,
_options
) do
outputs =
@ -382,7 +397,7 @@ defmodule Explorer.SmartContract.Reader do
method_id,
args || [],
from,
true,
leave_error_as_map,
abi
)
@ -390,7 +405,15 @@ defmodule Explorer.SmartContract.Reader do
%{output: outputs, names: names}
end
def query_function_with_names(contract_address_hash, %{method_id: method_id, args: args}, :proxy, from, _abi, options) do
def query_function_with_names(
contract_address_hash,
%{method_id: method_id, args: args},
:proxy,
from,
_abi,
leave_error_as_map,
options
) do
abi = get_abi(contract_address_hash, :proxy, options)
outputs =
@ -399,7 +422,7 @@ defmodule Explorer.SmartContract.Reader do
method_id,
args || [],
from,
true,
leave_error_as_map,
abi
)

@ -76,14 +76,14 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
code = Keyword.fetch!(params, :code)
optimize = Keyword.fetch!(params, :optimize)
optimization_runs = optimization_runs(params)
evm_version = Keyword.get(params, :evm_version, List.last(allowed_evm_versions()))
evm_version = Keyword.get(params, :evm_version, List.last(evm_versions(:solidity)))
bytecode_hash = Keyword.get(params, :bytecode_hash, "default")
external_libs = Keyword.get(params, :external_libs, %{})
external_libs_string = Jason.encode!(external_libs)
checked_evm_version =
if evm_version in allowed_evm_versions() do
if evm_version in evm_versions(:solidity) do
evm_version
else
"byzantium"
@ -262,9 +262,19 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
end
end
def allowed_evm_versions do
def evm_versions(compiler_type) do
case compiler_type do
:vyper ->
allowed_evm_versions(:allowed_vyper_evm_versions)
:solidity ->
allowed_evm_versions(:allowed_solidity_evm_versions)
end
end
defp allowed_evm_versions(env_name) do
:explorer
|> Application.get_env(:allowed_evm_versions)
|> Application.get_env(env_name)
|> String.split(",")
|> Enum.map(fn version -> String.trim(version) end)
end

@ -59,7 +59,7 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
if is_nil(params["name"]) or params["name"] == "" do
{:error, :name}
else
latest_evm_version = List.last(CodeCompiler.allowed_evm_versions())
latest_evm_version = List.last(CodeCompiler.evm_versions(:solidity))
evm_version = Map.get(params, "evm_version", latest_evm_version)
all_versions = [evm_version | previous_evm_versions(evm_version)]
@ -505,19 +505,19 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
end
def previous_evm_versions(current_evm_version) do
index = Enum.find_index(CodeCompiler.allowed_evm_versions(), fn el -> el == current_evm_version end)
index = Enum.find_index(CodeCompiler.evm_versions(:solidity), fn el -> el == current_evm_version end)
cond do
index == 0 ->
[]
index == 1 ->
[List.first(CodeCompiler.allowed_evm_versions())]
[List.first(CodeCompiler.evm_versions(:solidity))]
true ->
[
Enum.at(CodeCompiler.allowed_evm_versions(), index - 1),
Enum.at(CodeCompiler.allowed_evm_versions(), index - 2)
Enum.at(CodeCompiler.evm_versions(:solidity), index - 1),
Enum.at(CodeCompiler.evm_versions(:solidity), index - 2)
]
end
end

@ -153,7 +153,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"constant" => true,
"inputs" => [],
"name" => "get",
"outputs" => [%{"name" => "", "type" => "uint256", "value" => 0}],
"outputs" => [%{"type" => "uint256", "value" => 0}],
"payable" => _,
"stateMutability" => _,
"type" => _
@ -162,7 +162,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"constant" => true,
"inputs" => [%{"name" => "x", "type" => "uint256"}],
"name" => "with_arguments",
"outputs" => [%{"name" => "", "type" => "bool"}],
"outputs" => [%{"type" => "bool"}],
"payable" => _,
"stateMutability" => _,
"type" => _
@ -238,7 +238,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"constant" => true,
"inputs" => [],
"name" => "get",
"outputs" => [%{"name" => "", "type" => "uint256", "value" => 0}],
"outputs" => [%{"type" => "uint256", "value" => 0}],
"payable" => _,
"stateMutability" => _,
"type" => _
@ -247,7 +247,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"constant" => true,
"inputs" => [%{"name" => "x", "type" => "uint256"}],
"name" => "with_arguments",
"outputs" => [%{"name" => "", "type" => "bool"}],
"outputs" => [%{"type" => "bool"}],
"payable" => _,
"stateMutability" => _,
"type" => _

@ -353,17 +353,17 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
end
end
# describe "allowed_evm_versions/0" do
# describe "allowed_solidity_evm_versions/0" do
# test "returns allowed evm versions defined by ALLOWED_EVM_VERSIONS env var" do
# Application.put_env(:explorer, :allowed_evm_versions, "CustomEVM1,CustomEVM2,CustomEVM3")
# response = CodeCompiler.allowed_evm_versions()
# Application.put_env(:explorer, :allowed_solidity_evm_versions, "CustomEVM1,CustomEVM2,CustomEVM3")
# response = CodeCompiler.evm_versions(:solidity)
# assert ["CustomEVM1", "CustomEVM2", "CustomEVM3"] = response
# end
# test "returns allowed evm versions defined by not trimmed ALLOWED_EVM_VERSIONS env var" do
# Application.put_env(:explorer, :allowed_evm_versions, "CustomEVM1, CustomEVM2, CustomEVM3")
# response = CodeCompiler.allowed_evm_versions()
# Application.put_env(:explorer, :allowed_solidity_evm_versions, "CustomEVM1, CustomEVM2, CustomEVM3")
# response = CodeCompiler.evm_versions(:solidity)
# assert ["CustomEVM1", "CustomEVM2", "CustomEVM3"] = response
# end
@ -371,11 +371,11 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
# test "returns default_allowed_evm_versions" do
# Application.put_env(
# :explorer,
# :allowed_evm_versions,
# :allowed_solidity_evm_versions,
# "homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg"
# )
# response = CodeCompiler.allowed_evm_versions()
# response = CodeCompiler.evm_versions(:solidity)
# assert ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"] = response
# end

@ -170,9 +170,12 @@ exchange_rates_coin = System.get_env("EXCHANGE_RATES_COIN")
config :explorer,
coin: System.get_env("COIN") || exchange_rates_coin || "ETH",
coin_name: System.get_env("COIN_NAME") || exchange_rates_coin || "ETH",
allowed_evm_versions:
System.get_env("CONTRACT_VERIFICATION_ALLOWED_EVM_VERSIONS") ||
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,berlin,london,paris,default",
allowed_solidity_evm_versions:
System.get_env("CONTRACT_VERIFICATION_ALLOWED_SOLIDITY_EVM_VERSIONS") ||
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,berlin,london,paris,shanghai,default",
allowed_vyper_evm_versions:
System.get_env("CONTRACT_VERIFICATION_ALLOWED_VYPER_EVM_VERSIONS") ||
"byzantium,constantinople,petersburg,istanbul,berlin,paris,shanghai,default",
include_uncles_in_average_block_time: ConfigHelper.parse_bool_env_var("UNCLES_IN_AVERAGE_BLOCK_TIME"),
healthy_blocks_period: ConfigHelper.parse_time_env_var("HEALTHY_BLOCKS_PERIOD", "5m"),
realtime_events_sender:

@ -81,7 +81,8 @@ CACHE_ADDRESS_TRANSACTIONS_COUNTER_PERIOD=1800
CACHE_ADDRESS_TOKENS_USD_SUM_PERIOD=3600
CACHE_ADDRESS_TOKEN_TRANSFERS_COUNTER_PERIOD=1800
TOKEN_METADATA_UPDATE_INTERVAL=172800
CONTRACT_VERIFICATION_ALLOWED_EVM_VERSIONS=homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,berlin,london,paris,default
CONTRACT_VERIFICATION_ALLOWED_SOLIDITY_EVM_VERSIONS=homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,berlin,london,paris,shanghai,default
CONTRACT_VERIFICATION_ALLOWED_VYPER_EVM_VERSIONS=byzantium,constantinople,petersburg,istanbul,berlin,paris,shanghai,default
# CONTRACT_VERIFICATION_MAX_LIBRARIES=10
CONTRACT_MAX_STRING_LENGTH_WITHOUT_TRIMMING=2040
# CONTRACT_DISABLE_INTERACTION=

@ -690,8 +690,11 @@ endif
ifdef DECODE_NOT_A_CONTRACT_CALLS
BLOCKSCOUT_CONTAINER_PARAMS += -e 'DECODE_NOT_A_CONTRACT_CALLS=$(DECODE_NOT_A_CONTRACT_CALLS)'
endif
ifdef CONTRACT_VERIFICATION_ALLOWED_EVM_VERSIONS
BLOCKSCOUT_CONTAINER_PARAMS += -e 'CONTRACT_VERIFICATION_ALLOWED_EVM_VERSIONS=$(CONTRACT_VERIFICATION_ALLOWED_EVM_VERSIONS)'
ifdef CONTRACT_VERIFICATION_ALLOWED_SOLIDITY_EVM_VERSIONS
BLOCKSCOUT_CONTAINER_PARAMS += -e 'CONTRACT_VERIFICATION_ALLOWED_SOLIDITY_EVM_VERSIONS=$(CONTRACT_VERIFICATION_ALLOWED_SOLIDITY_EVM_VERSIONS)'
endif
ifdef CONTRACT_VERIFICATION_ALLOWED_VYPER_EVM_VERSIONS
BLOCKSCOUT_CONTAINER_PARAMS += -e 'CONTRACT_VERIFICATION_ALLOWED_VYPER_EVM_VERSIONS=$(CONTRACT_VERIFICATION_ALLOWED_VYPER_EVM_VERSIONS)'
endif
ifdef CONTRACT_VERIFICATION_MAX_LIBRARIES
BLOCKSCOUT_CONTAINER_PARAMS += -e 'CONTRACT_VERIFICATION_MAX_LIBRARIES=$(CONTRACT_VERIFICATION_MAX_LIBRARIES)'

Loading…
Cancel
Save