Fix read contract page

pull/4847/head
nikitosing 3 years ago
parent f94f79aebb
commit 6b131a8e09
  1. 2
      .dialyzer-ignore
  2. 2
      CHANGELOG.md
  3. 11
      apps/explorer/lib/explorer/chain.ex
  4. 2
      apps/explorer/lib/explorer/chain/block/reward.ex
  5. 2
      apps/explorer/lib/explorer/chain/supply/token_bridge.ex
  6. 4
      apps/explorer/lib/explorer/chain_spec/poa/importer.ex
  7. 90
      apps/explorer/lib/explorer/smart_contract/reader.ex
  8. 24
      apps/explorer/lib/explorer/staking/contract_state.ex
  9. 2
      apps/explorer/lib/explorer/token/instance_metadata_retriever.ex
  10. 2
      apps/explorer/lib/explorer/token/metadata_retriever.ex
  11. 22
      apps/explorer/lib/explorer/validator/metadata_retriever.ex
  12. 17
      apps/explorer/test/explorer/smart_contract/reader_test.exs

@ -19,7 +19,7 @@ lib/block_scout_web/views/layout_view.ex:145: The call 'Elixir.Poison.Parser':'p
lib/block_scout_web/views/layout_view.ex:237: 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:22
lib/explorer/smart_contract/reader.ex:447
lib/explorer/smart_contract/reader.ex:461
lib/indexer/fetcher/token_total_supply_on_demand.ex:16
lib/explorer/exchange_rates/source.ex:110
lib/explorer/exchange_rates/source.ex:113

@ -1,7 +1,7 @@
## Current
### Features
- [#4777](https://github.com/blockscout/blockscout/pull/4777), [#4791](https://github.com/blockscout/blockscout/pull/4791), [#4799](https://github.com/blockscout/blockscout/pull/4799) - Added decoding revert reason
- [#4777](https://github.com/blockscout/blockscout/pull/4777), [#4791](https://github.com/blockscout/blockscout/pull/4791), [#4799](https://github.com/blockscout/blockscout/pull/4799), [#4847](https://github.com/blockscout/blockscout/pull/4847) - Added decoding revert reason
- [#4776](https://github.com/blockscout/blockscout/pull/4776) - Added view for unsuccessfully fetched values from read functions
- [#4761](https://github.com/blockscout/blockscout/pull/4761) - ERC-1155 support
- [#4739](https://github.com/blockscout/blockscout/pull/4739) - Improve logs and inputs decoding

@ -6828,9 +6828,14 @@ defmodule Explorer.Chain do
defp get_implementation_address_hash_basic(proxy_address_hash, abi) do
# 5c60da1b = keccak256(implementation())
implementation_address =
case Reader.query_contract(proxy_address_hash, abi, %{
"5c60da1b" => []
}) do
case Reader.query_contract(
proxy_address_hash,
abi,
%{
"5c60da1b" => []
},
false
) do
%{"5c60da1b" => {:ok, [result]}} -> result
_ -> nil
end

@ -200,7 +200,7 @@ defmodule Explorer.Chain.Block.Reward do
|> Enum.map(fn {key, _value} -> key end)
|> List.first()
case Reader.query_contract(address, abi, params) do
case Reader.query_contract(address, abi, params, false) do
%{^method_id => {:ok, [result]}} -> result
_ -> @empty_address
end

@ -146,7 +146,7 @@ defmodule Explorer.Chain.Supply.TokenBridge do
|> Map.get("type", "")
value =
case Reader.query_contract(address, abi, params) do
case Reader.query_contract(address, abi, params, false) do
%{^method_id => {:ok, [result]}} ->
result

@ -98,10 +98,10 @@ defmodule Explorer.ChainSpec.POA.Importer do
|> Enum.map(fn {key, _value} -> key end)
|> List.first()
Reader.query_contract(address, abi, params)
Reader.query_contract(address, abi, params, false)
value =
case Reader.query_contract(address, abi, params) do
case Reader.query_contract(address, abi, params, false) do
%{^method_id => {:ok, [result]}} -> result
_ -> 0
end

@ -60,25 +60,32 @@ defmodule Explorer.SmartContract.Reader do
)
# => %{"sum" => {:error, "Data overflow encoding int, data `abc` cannot fit in 256 bits"}}
"""
@spec query_verified_contract(Hash.Address.t(), functions(), String.t() | nil, SmartContract.abi()) ::
@spec query_verified_contract(Hash.Address.t(), functions(), String.t() | nil, true | false, SmartContract.abi()) ::
functions_results()
def query_verified_contract(address_hash, functions, from, mabi) do
query_verified_contract_inner(address_hash, functions, mabi, from)
def query_verified_contract(address_hash, functions, from, leave_error_as_map, mabi) do
query_verified_contract_inner(address_hash, functions, mabi, from, leave_error_as_map)
end
@spec query_verified_contract(Hash.Address.t(), functions(), SmartContract.abi() | nil) :: functions_results()
def query_verified_contract(address_hash, functions, mabi \\ nil) do
query_verified_contract_inner(address_hash, functions, mabi, nil)
@spec query_verified_contract(Hash.Address.t(), functions(), true | false, SmartContract.abi() | nil) ::
functions_results()
def query_verified_contract(address_hash, functions, leave_error_as_map, mabi \\ nil) do
query_verified_contract_inner(address_hash, functions, mabi, nil, leave_error_as_map)
end
@spec query_verified_contract_inner(Hash.Address.t(), functions(), SmartContract.abi() | nil, String.t() | nil) ::
@spec query_verified_contract_inner(
Hash.Address.t(),
functions(),
SmartContract.abi() | nil,
String.t() | nil,
true | false
) ::
functions_results()
defp query_verified_contract_inner(address_hash, functions, mabi, from) do
defp query_verified_contract_inner(address_hash, functions, mabi, from, leave_error_as_map) do
contract_address = Hash.to_string(address_hash)
abi = prepare_abi(mabi, address_hash)
query_contract(contract_address, from, abi, functions)
query_contract(contract_address, from, abi, functions, leave_error_as_map)
end
defp prepare_abi(nil, address_hash) do
@ -103,20 +110,22 @@ defmodule Explorer.SmartContract.Reader do
@spec query_contract(
String.t(),
term(),
functions()
functions(),
true | false
) :: functions_results()
def query_contract(contract_address, abi, functions) do
query_contract_inner(contract_address, abi, functions, nil, nil, false)
def query_contract(contract_address, abi, functions, leave_error_as_map) do
query_contract_inner(contract_address, abi, functions, nil, nil, leave_error_as_map)
end
@spec query_contract(
String.t(),
String.t() | nil,
term(),
functions()
functions(),
true | false
) :: functions_results()
def query_contract(contract_address, from, abi, functions) do
query_contract_inner(contract_address, abi, functions, nil, from, true)
def query_contract(contract_address, from, abi, functions, leave_error_as_map) do
query_contract_inner(contract_address, abi, functions, nil, from, leave_error_as_map)
end
@spec query_contract_by_block_number(
@ -223,7 +232,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))
|> Enum.map(&fetch_current_value_from_blockchain(&1, abi_with_method_id, contract_address_hash, false))
end
end
@ -239,7 +248,9 @@ defmodule Explorer.SmartContract.Reader do
implementation_abi_with_method_id
|> Enum.filter(&Helper.queriable_method?(&1))
|> Enum.map(&fetch_current_value_from_blockchain(&1, implementation_abi_with_method_id, contract_address_hash))
|> Enum.map(
&fetch_current_value_from_blockchain(&1, implementation_abi_with_method_id, contract_address_hash, false)
)
end
end
@ -357,7 +368,7 @@ defmodule Explorer.SmartContract.Reader do
"tuple[#{tuple_types}]"
end
def fetch_current_value_from_blockchain(function, abi, contract_address_hash) do
def fetch_current_value_from_blockchain(function, abi, contract_address_hash, leave_error_as_map) do
values =
case function do
%{"inputs" => []} ->
@ -366,7 +377,7 @@ defmodule Explorer.SmartContract.Reader do
outputs = function["outputs"]
contract_address_hash
|> query_verified_contract(%{method_id => normalize_args(args)}, abi)
|> query_verified_contract(%{method_id => normalize_args(args)}, leave_error_as_map, abi)
|> link_outputs_and_values(outputs, method_id)
_ ->
@ -379,13 +390,14 @@ defmodule Explorer.SmartContract.Reader do
@doc """
Method performs query of read functions of a smart contract.
`type` could be :proxy or :reqular
if ethereumJSONRPC will return some errors it will represented as map
"""
@spec query_function_with_names(Hash.t(), %{method_id: String.t(), args: [term()] | nil}, atom(), String.t()) :: %{
:names => [any()],
:output => [%{}]
}
def query_function_with_names(contract_address_hash, %{method_id: method_id, args: args}, type, function_name) do
outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type)
outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type, true)
names = parse_names_from_abi(get_abi(contract_address_hash, type), function_name)
%{output: outputs, names: names}
end
@ -403,7 +415,7 @@ defmodule Explorer.SmartContract.Reader do
String.t()
) :: %{:names => [any()], :output => [%{}]}
def query_function_with_names(contract_address_hash, %{method_id: method_id, args: args}, type, function_name, from) do
outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from)
outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from, true)
names = parse_names_from_abi(get_abi(contract_address_hash, type), function_name)
%{output: outputs, names: names}
end
@ -411,28 +423,30 @@ defmodule Explorer.SmartContract.Reader do
@doc """
Fetches the blockchain value of a function that requires arguments.
"""
@spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom()) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type) do
query_function(contract_address_hash, %{method_id: method_id, args: []}, type)
@spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom(), true | false) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type, leave_error_as_map) do
query_function(contract_address_hash, %{method_id: method_id, args: []}, type, leave_error_as_map)
end
@spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom()) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: args}, type) do
query_function_inner(contract_address_hash, method_id, args, type, nil)
@spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom(), true | false) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: args}, type, leave_error_as_map) do
query_function_inner(contract_address_hash, method_id, args, type, nil, leave_error_as_map)
end
@spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom(), String.t() | nil) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type, from) do
query_function(contract_address_hash, %{method_id: method_id, args: []}, type, from)
@spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom(), String.t() | nil, true | false) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type, from, leave_error_as_map) do
query_function(contract_address_hash, %{method_id: method_id, args: []}, type, from, leave_error_as_map)
end
@spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom(), String.t() | nil) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from) do
query_function_inner(contract_address_hash, method_id, args, type, from)
@spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom(), String.t() | nil, true | false) :: [
%{}
]
def query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from, leave_error_as_map) do
query_function_inner(contract_address_hash, method_id, args, type, from, leave_error_as_map)
end
@spec query_function_inner(Hash.t(), String.t(), [term()], atom(), String.t() | nil) :: [%{}]
defp query_function_inner(contract_address_hash, method_id, args, type, from) do
@spec query_function_inner(Hash.t(), String.t(), [term()], atom(), String.t() | nil, true | false) :: [%{}]
defp query_function_inner(contract_address_hash, method_id, args, type, from, leave_error_as_map) do
abi = get_abi(contract_address_hash, type)
parsed_final_abi =
@ -441,7 +455,7 @@ defmodule Explorer.SmartContract.Reader do
%{outputs: outputs, method_id: method_id} = proccess_abi(parsed_final_abi, method_id)
query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id)
query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id, leave_error_as_map)
end
defp proccess_abi(nil, _method_id), do: nil
@ -454,9 +468,9 @@ defmodule Explorer.SmartContract.Reader do
%{outputs: outputs, method_id: method_id}
end
defp query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id) do
defp query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id, leave_error_as_map) do
contract_address_hash
|> query_verified_contract(%{method_id => normalize_args(args)}, from, abi)
|> query_verified_contract(%{method_id => normalize_args(args)}, from, leave_error_as_map, abi)
|> link_outputs_and_values(outputs, method_id)
end

@ -104,18 +104,28 @@ defmodule Explorer.Staking.ContractState do
"2d21d217" => {:ok, [token_contract_address]},
"dfc8bf4e" => {:ok, [validator_set_contract_address]}
} =
Reader.query_contract(staking_contract_address, staking_abi, %{
"#{erc_677_token_contract_signature}" => [],
"#{validator_set_contract_signature}" => []
})
Reader.query_contract(
staking_contract_address,
staking_abi,
%{
"#{erc_677_token_contract_signature}" => [],
"#{validator_set_contract_signature}" => []
},
false
)
# 56b54bae = keccak256(blockRewardContract())
block_reward_contract_signature = "56b54bae"
%{"56b54bae" => {:ok, [block_reward_contract_address]}} =
Reader.query_contract(validator_set_contract_address, validator_set_abi, %{
"#{block_reward_contract_signature}" => []
})
Reader.query_contract(
validator_set_contract_address,
validator_set_abi,
%{
"#{block_reward_contract_signature}" => []
},
false
)
state = %__MODULE__{
eth_blocknumber_pull_interval: eth_blocknumber_pull_interval,

@ -86,7 +86,7 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
end
def query_contract(contract_address_hash, contract_functions, abi) do
Reader.query_contract(contract_address_hash, abi, contract_functions)
Reader.query_contract(contract_address_hash, abi, contract_functions, false)
end
def fetch_json(uri) when uri in [%{@token_uri => {:ok, [""]}}, %{@uri => {:ok, [""]}}] do

@ -206,7 +206,7 @@ defmodule Explorer.Token.MetadataRetriever do
defp fetch_functions_with_retries(contract_address_hash, contract_functions, accumulator, retries_left)
when retries_left > 0 do
contract_functions_result = Reader.query_contract(contract_address_hash, @contract_abi, contract_functions)
contract_functions_result = Reader.query_contract(contract_address_hash, @contract_abi, contract_functions, false)
functions_with_errors =
Enum.filter(contract_functions_result, fn function ->

@ -17,9 +17,14 @@ defmodule Explorer.Validator.MetadataRetriever do
defp fetch_validators_list do
# b7ab4db5 = keccak256(getValidators())
case Reader.query_contract(config(:validators_contract_address), contract_abi("validators.json"), %{
"b7ab4db5" => []
}) do
case Reader.query_contract(
config(:validators_contract_address),
contract_abi("validators.json"),
%{
"b7ab4db5" => []
},
false
) do
%{"b7ab4db5" => {:ok, [validators]}} -> validators
_ -> []
end
@ -28,9 +33,14 @@ defmodule Explorer.Validator.MetadataRetriever do
defp fetch_validator_metadata(validator_address) do
# fa52c7d8 = keccak256(validators(address))
%{"fa52c7d8" => {:ok, fields}} =
Reader.query_contract(config(:metadata_contract_address), contract_abi("metadata.json"), %{
"fa52c7d8" => [validator_address]
})
Reader.query_contract(
config(:metadata_contract_address),
contract_abi("metadata.json"),
%{
"fa52c7d8" => [validator_address]
},
false
)
fields
end

@ -21,7 +21,7 @@ defmodule Explorer.SmartContract.ReaderTest do
blockchain_get_function_mock()
response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []})
response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []}, false)
assert %{"6d4ce63c" => {:ok, [0]}} == response
end
@ -44,7 +44,7 @@ defmodule Explorer.SmartContract.ReaderTest do
string_argument = %{"a50e1860" => ["abc"]}
response = Reader.query_contract(contract_address_hash, [int_function_abi], string_argument)
response = Reader.query_contract(contract_address_hash, [int_function_abi], string_argument, false)
assert %{"a50e1860" => {:error, "Data overflow encoding int, data `abc` cannot fit in 256 bits"}} = response
end
@ -62,7 +62,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end
)
response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []})
response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []}, false)
assert %{"6d4ce63c" => {:error, "(12345) Error message"}} = response
end
@ -80,7 +80,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end
)
response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []})
response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []}, false)
assert %{"6d4ce63c" => {:error, "Bad gateway"}} = response
end
@ -98,7 +98,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end
)
response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []})
response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []}, false)
assert %{"6d4ce63c" => {:error, "no function clause matches"}} = response
end
@ -113,7 +113,7 @@ defmodule Explorer.SmartContract.ReaderTest do
blockchain_get_function_mock()
assert Reader.query_verified_contract(hash, %{"6d4ce63c" => []}) == %{"6d4ce63c" => {:ok, [0]}}
assert Reader.query_verified_contract(hash, %{"6d4ce63c" => []}, false) == %{"6d4ce63c" => {:ok, [0]}}
end
end
@ -264,7 +264,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"type" => "uint256",
"value" => 0
}
] = Reader.query_function(smart_contract.address_hash, %{method_id: "6d4ce63c", args: []}, :regular)
] = Reader.query_function(smart_contract.address_hash, %{method_id: "6d4ce63c", args: []}, :regular, false)
end
test "nil arguments is treated as []" do
@ -277,7 +277,8 @@ defmodule Explorer.SmartContract.ReaderTest do
"type" => "uint256",
"value" => 0
}
] = Reader.query_function(smart_contract.address_hash, %{method_id: "6d4ce63c", args: nil}, :regular)
] =
Reader.query_function(smart_contract.address_hash, %{method_id: "6d4ce63c", args: nil}, :regular, false)
end
end

Loading…
Cancel
Save