Merge pull request #7952 from blockscout/np-sourcify-constructor-args

Add parsing constructor arguments for sourcify contracts
pull/8140/head
Victor Baranov 1 year ago committed by GitHub
commit 3ebf44568d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  3. 35
      apps/explorer/lib/explorer/chain.ex
  4. 51
      apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
  5. 3
      apps/explorer/test/explorer/chain/csv_export/address_log_csv_exporter_test.exs

@ -4,6 +4,7 @@
### Features ### Features
- [#7952](https://github.com/blockscout/blockscout/pull/7952) - Add parsing constructor arguments for sourcify contracts
- [#6190](https://github.com/blockscout/blockscout/pull/6190) - Add EIP-1559 support to gas price oracle - [#6190](https://github.com/blockscout/blockscout/pull/6190) - Add EIP-1559 support to gas price oracle
- [#7977](https://github.com/blockscout/blockscout/pull/7977) - GraphQL: extend schema with new field for existing objects - [#7977](https://github.com/blockscout/blockscout/pull/7977) - GraphQL: extend schema with new field for existing objects

@ -43,7 +43,7 @@
<% end %> <% end %>
<%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %>
<% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %>
<%= if @address.smart_contract.partially_verified && smart_contract_verified do %> <%= if @address.smart_contract.verified_via_sourcify && @address.smart_contract.partially_verified && smart_contract_verified do %>
<div class="mb-4"> <div class="mb-4">
<i style="color: #f7b32b;" class="fa fa-info-circle"></i><span> <%= gettext("This contract has been partially verified via Sourcify.") %> <i style="color: #f7b32b;" class="fa fa-info-circle"></i><span> <%= gettext("This contract has been partially verified via Sourcify.") %>
<% else %> <% else %>

@ -98,6 +98,7 @@ defmodule Explorer.Chain do
alias Explorer.Market.MarketHistoryCache alias Explorer.Market.MarketHistoryCache
alias Explorer.{PagingOptions, Repo} alias Explorer.{PagingOptions, Repo}
alias Explorer.SmartContract.Helper alias Explorer.SmartContract.Helper
alias Explorer.SmartContract.Solidity.Verifier
alias Explorer.Tags.{AddressTag, AddressToTag} alias Explorer.Tags.{AddressTag, AddressToTag}
alias Dataloader.Ecto, as: DataloaderEcto alias Dataloader.Ecto, as: DataloaderEcto
@ -1913,7 +1914,7 @@ defmodule Explorer.Chain do
if smart_contract do if smart_contract do
CheckBytecodeMatchingOnDemand.trigger_check(address_result, smart_contract) CheckBytecodeMatchingOnDemand.trigger_check(address_result, smart_contract)
LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, smart_contract) LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, smart_contract)
address_result check_and_update_constructor_args(address_result)
else else
LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, nil) LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, nil)
@ -1936,6 +1937,36 @@ defmodule Explorer.Chain do
end end
end end
defp check_and_update_constructor_args(
%SmartContract{address_hash: address_hash, constructor_arguments: nil, verified_via_sourcify: true} =
smart_contract
) do
if args = Verifier.parse_constructor_arguments_for_sourcify_contract(address_hash, smart_contract.abi) do
smart_contract |> SmartContract.changeset(%{constructor_arguments: args}) |> Repo.update()
%SmartContract{smart_contract | constructor_arguments: args}
else
smart_contract
end
end
defp check_and_update_constructor_args(
%Address{
hash: address_hash,
contract_code: deployed_bytecode,
smart_contract: %SmartContract{constructor_arguments: nil, verified_via_sourcify: true} = smart_contract
} = address
) do
if args =
Verifier.parse_constructor_arguments_for_sourcify_contract(address_hash, smart_contract.abi, deployed_bytecode) do
smart_contract |> SmartContract.changeset(%{constructor_arguments: args}) |> Repo.update()
%Address{address | smart_contract: %SmartContract{smart_contract | constructor_arguments: args}}
else
address
end
end
defp check_and_update_constructor_args(other), do: other
defp add_twin_info_to_contract(address_result, address_verified_twin_contract, _hash) defp add_twin_info_to_contract(address_result, address_verified_twin_contract, _hash)
when is_nil(address_verified_twin_contract), when is_nil(address_verified_twin_contract),
do: address_result do: address_result
@ -4310,7 +4341,7 @@ defmodule Explorer.Chain do
verified_contract_twin_additional_sources = get_contract_additional_sources(verified_contract_twin, options) verified_contract_twin_additional_sources = get_contract_additional_sources(verified_contract_twin, options)
%{ %{
:verified_contract => verified_contract_twin, :verified_contract => check_and_update_constructor_args(verified_contract_twin),
:additional_sources => verified_contract_twin_additional_sources :additional_sources => verified_contract_twin_additional_sources
} }
else else

@ -11,8 +11,10 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
import Explorer.SmartContract.Helper, import Explorer.SmartContract.Helper,
only: [cast_libraries: 1, prepare_bytecode_for_microservice: 3, contract_creation_input: 1] only: [cast_libraries: 1, prepare_bytecode_for_microservice: 3, contract_creation_input: 1]
# import Explorer.Chain.SmartContract, only: [:function_description]
alias ABI.{FunctionSelector, TypeDecoder} alias ABI.{FunctionSelector, TypeDecoder}
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.{Data, Hash, SmartContract}
alias Explorer.SmartContract.RustVerifierInterface alias Explorer.SmartContract.RustVerifierInterface
alias Explorer.SmartContract.Solidity.CodeCompiler alias Explorer.SmartContract.Solidity.CodeCompiler
@ -533,4 +535,53 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
def parse_boolean(false), do: false def parse_boolean(false), do: false
def parse_boolean(_), do: false def parse_boolean(_), do: false
@doc """
Function tries to parse constructor args from smart contract creation input.
1. using `extract_meta_from_deployed_bytecode/1` we derive CBOR metadata string
2. using metadata we split creation_tx_input and try to decode resulting constructor arguments
2.1. if we successfully decoded args using constructor's abi, then return constructor args
2.2 otherwise return nil
"""
@spec parse_constructor_arguments_for_sourcify_contract(Hash.Address.t(), SmartContract.abi()) :: nil | binary
def parse_constructor_arguments_for_sourcify_contract(address_hash, abi) do
parse_constructor_arguments_for_sourcify_contract(address_hash, abi, Chain.smart_contract_bytecode(address_hash))
end
@doc """
Clause for cases when we already can pass deployed bytecode to this function (in order to avoid excessive read DB accesses)
"""
@spec parse_constructor_arguments_for_sourcify_contract(
Hash.Address.t(),
SmartContract.abi(),
binary | Explorer.Chain.Data.t()
) :: nil | binary
def parse_constructor_arguments_for_sourcify_contract(address_hash, abi, deployed_bytecode)
when is_binary(deployed_bytecode) do
creation_tx_input =
case Chain.smart_contract_creation_tx_bytecode(address_hash) do
%{init: init, created_contract_code: _created_contract_code} ->
"0x" <> init_without_0x = init
init_without_0x
_ ->
nil
end
with true <- has_constructor_with_params?(abi),
check_function <- parse_constructor_and_return_check_function(abi),
false <- is_nil(creation_tx_input) || deployed_bytecode == "0x",
{meta, meta_length} <- extract_meta_from_deployed_bytecode(deployed_bytecode),
[_bytecode, constructor_args] <- String.split(creation_tx_input, meta <> meta_length),
^constructor_args <- check_function.(constructor_args) do
constructor_args
else
_ ->
nil
end
end
def parse_constructor_arguments_for_sourcify_contract(address_hash, abi, deployed_bytecode) do
parse_constructor_arguments_for_sourcify_contract(address_hash, abi, Data.to_string(deployed_bytecode))
end
end end

@ -1,6 +1,7 @@
defmodule Explorer.Chain.AddressLogCsvExporterTest do defmodule Explorer.Chain.AddressLogCsvExporterTest do
use Explorer.DataCase use Explorer.DataCase
alias Explorer.Chain.Address
alias Explorer.Chain.CSVExport.AddressLogCsvExporter alias Explorer.Chain.CSVExport.AddressLogCsvExporter
describe "export/3" do describe "export/3" do
@ -74,7 +75,7 @@ defmodule Explorer.Chain.AddressLogCsvExporterTest do
assert result.index == to_string(log.index) assert result.index == to_string(log.index)
assert result.block_number == to_string(log.block_number) assert result.block_number == to_string(log.block_number)
assert result.block_hash == to_string(log.block_hash) assert result.block_hash == to_string(log.block_hash)
assert result.address == String.downcase(to_string(log.address)) assert result.address == Address.checksum(log.address.hash)
assert result.data == to_string(log.data) assert result.data == to_string(log.data)
assert result.first_topic == to_string(log.first_topic) assert result.first_topic == to_string(log.first_topic)
assert result.second_topic == to_string(log.second_topic) assert result.second_topic == to_string(log.second_topic)

Loading…
Cancel
Save