Merge pull request #4847 from blockscout/np-fix-reqd-contract-page

Fix read contract page
pull/4807/head
Victor Baranov 3 years ago committed by GitHub
commit c251d76b3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  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/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:21
lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:22 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/indexer/fetcher/token_total_supply_on_demand.ex:16
lib/explorer/exchange_rates/source.ex:110 lib/explorer/exchange_rates/source.ex:110
lib/explorer/exchange_rates/source.ex:113 lib/explorer/exchange_rates/source.ex:113

@ -1,7 +1,7 @@
## Current ## Current
### Features ### 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 - [#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 - [#4761](https://github.com/blockscout/blockscout/pull/4761) - ERC-1155 support
- [#4739](https://github.com/blockscout/blockscout/pull/4739) - Improve logs and inputs decoding - [#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 defp get_implementation_address_hash_basic(proxy_address_hash, abi) do
# 5c60da1b = keccak256(implementation()) # 5c60da1b = keccak256(implementation())
implementation_address = implementation_address =
case Reader.query_contract(proxy_address_hash, abi, %{ case Reader.query_contract(
"5c60da1b" => [] proxy_address_hash,
}) do abi,
%{
"5c60da1b" => []
},
false
) do
%{"5c60da1b" => {:ok, [result]}} -> result %{"5c60da1b" => {:ok, [result]}} -> result
_ -> nil _ -> nil
end end

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

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

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

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

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

@ -86,7 +86,7 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
end end
def query_contract(contract_address_hash, contract_functions, abi) do 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 end
def fetch_json(uri) when uri in [%{@token_uri => {:ok, [""]}}, %{@uri => {:ok, [""]}}] do 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) defp fetch_functions_with_retries(contract_address_hash, contract_functions, accumulator, retries_left)
when retries_left > 0 do 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 = functions_with_errors =
Enum.filter(contract_functions_result, fn function -> Enum.filter(contract_functions_result, fn function ->

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

@ -21,7 +21,7 @@ defmodule Explorer.SmartContract.ReaderTest do
blockchain_get_function_mock() 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 assert %{"6d4ce63c" => {:ok, [0]}} == response
end end
@ -44,7 +44,7 @@ defmodule Explorer.SmartContract.ReaderTest do
string_argument = %{"a50e1860" => ["abc"]} 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 assert %{"a50e1860" => {:error, "Data overflow encoding int, data `abc` cannot fit in 256 bits"}} = response
end end
@ -62,7 +62,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end 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 assert %{"6d4ce63c" => {:error, "(12345) Error message"}} = response
end end
@ -80,7 +80,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end 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 assert %{"6d4ce63c" => {:error, "Bad gateway"}} = response
end end
@ -98,7 +98,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end 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 assert %{"6d4ce63c" => {:error, "no function clause matches"}} = response
end end
@ -113,7 +113,7 @@ defmodule Explorer.SmartContract.ReaderTest do
blockchain_get_function_mock() 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
end end
@ -264,7 +264,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"type" => "uint256", "type" => "uint256",
"value" => 0 "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 end
test "nil arguments is treated as []" do test "nil arguments is treated as []" do
@ -277,7 +277,8 @@ defmodule Explorer.SmartContract.ReaderTest do
"type" => "uint256", "type" => "uint256",
"value" => 0 "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
end end

Loading…
Cancel
Save