Implement new contracts verification approach

pull/5479/head
nikitosing 3 years ago
parent edded3e022
commit d717593d76
  1. 2
      .dialyzer-ignore
  2. 1
      CHANGELOG.md
  3. 3
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  4. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
  5. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex
  6. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex
  7. 32
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex
  8. 33
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex
  9. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex
  10. 15
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex
  11. 14
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex
  12. 1
      apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_flattened_code_view.ex
  13. 2
      apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_standard_json_input_view.ex
  14. 1
      apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex
  15. 54
      apps/block_scout_web/priv/gettext/default.pot
  16. 54
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  17. 65
      apps/explorer/lib/explorer/chain/smart_contract.ex
  18. 58
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  19. 3
      apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
  20. 449
      apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
  21. 525
      apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
  22. 3
      apps/explorer/mix.exs
  23. 5
      apps/explorer/priv/compile_solc.js
  24. 15
      apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs
  25. 347
      apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs
  26. 1822
      apps/explorer/test/explorer/smart_contract/verifier/constructor_arguments_test.exs
  27. 73
      apps/explorer/test/support/factory.ex
  28. 1163
      apps/explorer/test/support/fixture/smart_contract/ERC677.sol
  29. 6
      apps/explorer/test/support/fixture/smart_contract/contract_with_lib.json
  30. 259
      apps/explorer/test/support/fixture/smart_contract/home_bridge.sol
  31. 1718
      apps/explorer/test/support/fixture/smart_contract/issue_4758.sol
  32. 28
      apps/explorer/test/support/fixture/smart_contract/issue_5431.sol
  33. 4
      apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json
  34. 1
      mix.lock

@ -22,7 +22,7 @@ lib/explorer/smart_contract/reader.ex:435
lib/indexer/fetcher/token_total_supply_on_demand.ex:16
lib/explorer/exchange_rates/source.ex:110
lib/explorer/exchange_rates/source.ex:113
lib/explorer/smart_contract/solidity/verifier.ex:162
lib/explorer/smart_contract/solidity/verifier.ex:199
lib/block_scout_web/templates/address_contract/index.html.eex:158
lib/block_scout_web/templates/address_contract/index.html.eex:195
lib/explorer/staking/stake_snapshotting.ex:15: Function do_snapshotting/7 has no local return

@ -1,6 +1,7 @@
## Current
### Features
- [#5479](https://github.com/blockscout/blockscout/pull/5479) - Remake of solidity verifier module; Verification UX improvements
- [#5540](https://github.com/blockscout/blockscout/pull/5540) - Tx page: scroll to selected tab's data
### Fixes

@ -67,7 +67,8 @@ defmodule BlockScoutWeb.Notifier do
compiler_versions: compiler_versions,
evm_versions: CodeCompiler.allowed_evm_versions(),
address_hash: address_hash,
conn: conn
conn: conn,
retrying: true
)
{:error, result}

@ -9,7 +9,7 @@
[],
fn f -> %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", address_hash: @address_hash, f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">

@ -2,7 +2,7 @@
<div class="smart-contract-form-group-inner-wrapper">
<%= label @f, :compiler_version, gettext("Compiler") %>
<div class="center-column">
<%= select @f, :compiler_version, @compiler_versions, class: "form-control border-rounded", selected: @compiler_version, "aria-describedby": "compiler-help-block", id: "smart_contract_compiler_version" %>
<%= select @f, :compiler_version, @compiler_versions, class: "form-control border-rounded", "aria-describedby": "compiler-help-block", id: "smart_contract_compiler_version" %>
<%= error_tag @f, :compiler_version, id: "compiler-help-block", class: "text-danger form-error" %>
</div>
<div class="smart-contract-form-group-tooltip"><%= raw @tooltip %></div>

@ -2,7 +2,7 @@
<div class="smart-contract-form-group-inner-wrapper">
<%= label @f, :name, gettext("Contract Name") %>
<div class="center-column">
<%= text_input @f, :name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block", "data-test": "contract_name", value: @contract_name_value %>
<%= text_input @f, :name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block", "data-test": "contract_name" %>
<%= error_tag @f, :name, id: "contract-name-help-block", class: "text-danger form-error" %>
</div>
<div class="smart-contract-form-group-tooltip"><%= raw @tooltip %></div>

@ -1,20 +1,20 @@
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label @f, "Try to fetch constructor arguments automatically" %>
<div class="center-column">
<div class="form-radios-group">
<div class="radio-big">
<%= radio_button @f, :autodetect_constructor_args, false, checked: !@fetch_constructor_arguments_automatically, class: "form-check-input autodetectfalse" %>
<div class="radio-icon"></div>
<%= label :autodetect_constructor_args, :false, gettext("No"), class: "radio-text" %>
</div>
<div class="radio-big">
<%= radio_button @f, :autodetect_constructor_args, true, checked: @fetch_constructor_arguments_automatically, class: "form-check-input autodetecttrue", "aria-describedby": "autodetect_constructor_args-help-block" %>
<div class="radio-icon"></div>
<%= label :autodetect_constructor_args, :true, gettext("Yes"), class: "radio-text" %>
</div>
</div>
<%= error_tag @f, :autodetect_constructor_args, id: "autodetect_constructor_args-help-block", class: "text-danger form-error" %>
<div class="smart-contract-form-group-inner-wrapper">
<%= label @f, "Try to fetch constructor arguments automatically" %>
<div class="center-column">
<div class="form-radios-group">
<div class="radio-big">
<%= radio_button @f, :autodetect_constructor_args, false, class: "form-check-input autodetectfalse" %>
<div class="radio-icon"></div>
<%= label :autodetect_constructor_args, :false, gettext("No"), class: "radio-text" %>
</div>
<div class="radio-big">
<%= radio_button @f, :autodetect_constructor_args, true, class: "form-check-input autodetecttrue", "aria-describedby": "autodetect_constructor_args-help-block" %>
<div class="radio-icon"></div>
<%= label :autodetect_constructor_args, :true, gettext("Yes"), class: "radio-text" %>
</div>
</div>
<%= error_tag @f, :autodetect_constructor_args, id: "autodetect_constructor_args-help-block", class: "text-danger form-error" %>
</div>
</div>
</div>

@ -1,11 +1,6 @@
<% metadata_for_verification = Chain.get_address_verified_twin_contract(@address_hash).verified_contract %>
<% contract_name_value = if metadata_for_verification, do: metadata_for_verification.name, else: "" %>
<% optimization_runs_value = if metadata_for_verification, do: metadata_for_verification.optimization_runs, else: "200" %>
<% optimization = if metadata_for_verification, do: metadata_for_verification.optimization, else: true %>
<% evm_version = if metadata_for_verification, do: metadata_for_verification.evm_version, else: "default" %>
<% compiler_version = if metadata_for_verification, do: metadata_for_verification.compiler_version, else: "latest" %>
<% contract_source_code_value = if metadata_for_verification, do: metadata_for_verification.contract_source_code, else: "" %>
<% fetch_constructor_arguments_automatically = if metadata_for_verification, do: true, else: false %>
<% metadata_for_verification = if assigns[:retrying], do: nil, else: Chain.get_address_verified_twin_contract(@address_hash).verified_contract %>
<% changeset = if assigns[:retrying], do: @changeset, else: SmartContract.merge_twin_contract_with_changeset(metadata_for_verification, @changeset) %>
<% fetch_constructor_arguments_automatically = if metadata_for_verification, do: true, else: changeset.changes.autodetect_constructor_args %>
<% display_constructor_arguments_text_area = if fetch_constructor_arguments_automatically, do: "none", else: "block" %>
<section data-page="contract-verification" class="container new-smart-contract-container">
<%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %>
@ -13,24 +8,24 @@
<div class="new-smart-contract-form">
<h1 class="smart-contract-title"><%= gettext "New Solidity Smart Contract Verification" %></h1>
<%= form_for @changeset,
<%= form_for changeset,
address_contract_verification_path(@conn, :create),
[],
fn f -> %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", address_hash: @address_hash, f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code. For example, in <span class=\"tooltip-quote\">contract MyContract {..}</span> <strong>MyContract</strong> is the contract name.", contract_name_value: contract_name_value %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code. For example, in <span class=\"tooltip-quote\">contract MyContract {..}</span> <strong>MyContract</strong> is the contract name." %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_include_nightly_builds_field.html", f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_version: compiler_version, compiler_versions: @compiler_versions, tooltip: "The compiler version is specified in <span class=\"tooltip-quote\">pragma solidity X.X.X</span>. Use the compiler version rather than the nightly build. If using the Solidity compiler, run <span class=\"tooltip-quote\">solc —version</span> to check." %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_versions: @compiler_versions, tooltip: "The compiler version is specified in <span class=\"tooltip-quote\">pragma solidity X.X.X</span>. Use the compiler version rather than the nightly build. If using the Solidity compiler, run <span class=\"tooltip-quote\">solc —version</span> to check." %>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label :evm_version, :evm_version, gettext("EVM Version") %>
<div class="center-column">
<%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", selected: evm_version, "aria-describedby": "evm-version-help-block" %>
<%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", "aria-describedby": "evm-version-help-block" %>
</div>
<div class="smart-contract-form-group-tooltip">The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version. <a href="https://forum.poa.network/t/smart-contract-verification-evm-version-details/2318" target="_blank">EVM version details</a>.</div>
</div>
@ -42,12 +37,12 @@
<div class="center-column">
<div class="form-radios-group">
<div class="radio-big">
<%= radio_button f, :optimization, false, checked: !optimization, class: "form-check-input optimization-false" %>
<%= radio_button f, :optimization, false, class: "form-check-input optimization-false" %>
<div class="radio-icon"></div>
<%= label :smart_contract_optimization, :false, gettext("No"), class: "radio-text" %>
</div>
<div class="radio-big">
<%= radio_button f, :optimization, true, checked: optimization, class: "form-check-input optimization-true", "aria-describedby": "optimization-help-block" %>
<%= radio_button f, :optimization, true, class: "form-check-input optimization-true", "aria-describedby": "optimization-help-block" %>
<div class="radio-icon"></div>
<%= label :smart_contract_optimization, :true, gettext("Yes"), class: "radio-text" %>
</div>
@ -58,11 +53,11 @@
</div>
</div>
<div class="smart-contract-form-group optimization-runs">
<div class="smart-contract-form-group optimization-runs" style="<%= if !changeset.changes.optimization, do: "display: none;"%>">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :name, gettext("Optimization runs") %>
<div class="center-column">
<%= text_input f, :optimization_runs, value: 200, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs", value: optimization_runs_value %>
<%= text_input f, :optimization_runs, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs" %>
</div>
<div class="smart-contract-form-group-tooltip"></div>
</div>
@ -72,14 +67,14 @@
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :contract_source_code, gettext("Enter the Solidity Contract Code") %>
<div class="center-column">
<%= textarea f, :contract_source_code, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-source-code-help-block", value: contract_source_code_value %>
<%= textarea f, :contract_source_code, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-source-code-help-block" %>
<%= error_tag f, :contract_source_code, id: "contract-source-code-help-block", class: "text-danger form-error", "data-test": "contract-source-code-error" %>
</div>
<div class="smart-contract-form-group-tooltip">We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the <a href="https://github.com/poanetwork/solidity-flattener" target="_blank">POA solidity flattener or the <a href="https://www.npmjs.com/package/truffle-flattener" target="_blank">truffle flattener</a>.</div>
</div>
</div>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_fetch_constructor_args.html", f: f, fetch_constructor_arguments_automatically: fetch_constructor_arguments_automatically %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_fetch_constructor_args.html", f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_constructor_args.html", f: f, display_constructor_arguments_text_area: display_constructor_arguments_text_area %>

@ -7,7 +7,7 @@
address_contract_verification_path(@conn, :create),
[id: "metadata-json-dropzone-form"],
fn f -> %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", address_hash: @address_hash, f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">

@ -1,24 +1,23 @@
<% metadata_for_verification = Chain.get_address_verified_twin_contract(@address_hash).verified_contract %>
<% contract_name_value = if metadata_for_verification, do: metadata_for_verification.name, else: "" %>
<% compiler_version = if metadata_for_verification, do: metadata_for_verification.compiler_version, else: "latest" %>
<% fetch_constructor_arguments_automatically = if metadata_for_verification, do: true, else: false %>
<% changeset = if assigns[:retrying], do: @changeset, else: SmartContract.merge_twin_contract_with_changeset(metadata_for_verification, @changeset) %>
<% fetch_constructor_arguments_automatically = if metadata_for_verification, do: true, else: changeset.changes.autodetect_constructor_args %>
<% display_constructor_arguments_text_area = if fetch_constructor_arguments_automatically, do: "none", else: "block" %>
<section data-page="contract-verification" class="container new-smart-contract-container">
<%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %>
<div class="new-smart-contract-form">
<h1 class="smart-contract-title"><%= gettext "New Smart Contract Verification" %></h1>
<%= form_for @changeset,
<%= form_for changeset,
address_contract_verification_path(@conn, :create),
[id: "standard-json-dropzone-form"],
fn f -> %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", address_hash: @address_hash, f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code. For example, in <span class=\"tooltip-quote\">contract MyContract {..}</span> <strong>MyContract</strong> is the contract name. Also contract name could be: <span class=\"tooltip-quote\"><strong>path/to/file.sol:MyContract</strong></span>", contract_name_value: contract_name_value %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code. For example, in <span class=\"tooltip-quote\">contract MyContract {..}</span> <strong>MyContract</strong> is the contract name. Also contract name could be: <span class=\"tooltip-quote\"><strong>path/to/file.sol:MyContract</strong></span>" %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_include_nightly_builds_field.html", f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_version: compiler_version, compiler_versions: @compiler_versions, tooltip: "The compiler version is specified in <span class=\"tooltip-quote\">pragma solidity X.X.X</span>. Use the compiler version rather than the nightly build. If using the Solidity compiler, run <span class=\"tooltip-quote\">solc —version</span> to check." %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_versions: @compiler_versions, tooltip: "The compiler version is specified in <span class=\"tooltip-quote\">pragma solidity X.X.X</span>. Use the compiler version rather than the nightly build. If using the Solidity compiler, run <span class=\"tooltip-quote\">solc —version</span> to check." %>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
@ -35,7 +34,7 @@
</div>
</div>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_fetch_constructor_args.html", f: f, fetch_constructor_arguments_automatically: fetch_constructor_arguments_automatically %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_fetch_constructor_args.html", f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_constructor_args.html", f: f, display_constructor_arguments_text_area: display_constructor_arguments_text_area %>

@ -1,29 +1,27 @@
<% metadata_for_verification = Chain.get_address_verified_twin_contract(@address_hash).verified_contract %>
<% contract_name_value = if metadata_for_verification, do: metadata_for_verification.name, else: "Vyper_contract" %>
<% compiler_version = if metadata_for_verification, do: metadata_for_verification.compiler_version, else: "latest" %>
<% contract_source_code_value = if metadata_for_verification, do: metadata_for_verification.contract_source_code, else: "" %>
<% changeset = if assigns[:retrying], do: @changeset, else: SmartContract.merge_twin_vyper_contract_with_changeset(metadata_for_verification, @changeset) %>
<section data-page="contract-verification" class="container new-smart-contract-container">
<%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %>
<div class="new-smart-contract-form">
<h1 class="smart-contract-title"><%= gettext "New Vyper Smart Contract Verification" %></h1>
<%= form_for @changeset,
<%= form_for changeset,
address_contract_verification_path(@conn, :create),
[],
fn f -> %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", address_hash: @address_hash, f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code.", contract_name_value: contract_name_value %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code." %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_version: compiler_version, compiler_versions: @compiler_versions, tooltip: "" %>
<%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_versions: @compiler_versions, tooltip: "" %>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :contract_source_code, gettext("Enter the Vyper Contract Code") %>
<div class="center-column">
<%= textarea f, :contract_source_code, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-source-code-help-block", value: contract_source_code_value %>
<%= textarea f, :contract_source_code, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-source-code-help-block" %>
<%= error_tag f, :contract_source_code, id: "contract-source-code-help-block", class: "text-danger form-error", "data-test": "contract-source-code-error" %>
</div>
<div class="smart-contract-form-group-tooltip"></div>

@ -2,4 +2,5 @@ defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView do
use BlockScoutWeb, :view
alias Explorer.Chain
alias Explorer.Chain.SmartContract
end

@ -1,4 +1,6 @@
defmodule BlockScoutWeb.AddressContractVerificationViaStandardJsonInputView do
use BlockScoutWeb, :view
alias Explorer.Chain
alias Explorer.Chain.SmartContract
end

@ -2,4 +2,5 @@ defmodule BlockScoutWeb.AddressContractVerificationVyperView do
use BlockScoutWeb, :view
alias Explorer.Chain
alias Explorer.Chain.SmartContract
end

@ -488,10 +488,10 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:105
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:150
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:145
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:41
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:56
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:53 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:55
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:51 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
msgstr ""
@ -611,10 +611,10 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:2
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:11
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:6
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:2
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:7
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:6 lib/block_scout_web/templates/tokens/holder/index.html.eex:15
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:6
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:4 lib/block_scout_web/templates/tokens/holder/index.html.eex:15
msgid "Connection Lost"
msgstr ""
@ -684,7 +684,7 @@ msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:91
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:86
msgid "Contract Libraries"
msgstr ""
@ -990,7 +990,7 @@ msgid "Drop sources and metadata JSON file or click here"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:29
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:28
msgid "Drop the standard input JSON file or click here"
msgstr ""
@ -1033,7 +1033,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:76
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:31
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:26
msgid "EVM Version"
msgstr ""
@ -1053,12 +1053,12 @@ msgid "Emission Reward"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:73
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:68
msgid "Enter the Solidity Contract Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:24
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:22
msgid "Enter the Vyper Contract Code"
msgstr ""
@ -1470,10 +1470,10 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:70
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:144
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:139
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:35
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:50
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:47 lib/block_scout_web/templates/address_read_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:49
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:45 lib/block_scout_web/templates/address_read_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_read_proxy/index.html.eex:12 lib/block_scout_web/templates/address_write_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_write_proxy/index.html.eex:12 lib/block_scout_web/templates/tokens/contract/index.html.eex:16
msgid "Loading..."
@ -1657,17 +1657,17 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:5
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:5
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:10
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:9
msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:14
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:9
msgid "New Solidity Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:9
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:7
msgid "New Vyper Smart Contract Verification"
msgstr ""
@ -1686,7 +1686,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:9
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:9
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:47 lib/block_scout_web/templates/stakes/_rows.html.eex:24
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:42 lib/block_scout_web/templates/stakes/_rows.html.eex:24
msgid "No"
msgstr ""
@ -1754,7 +1754,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:63
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:58
msgid "Optimization runs"
msgstr ""
@ -1975,10 +1975,10 @@ msgid "Request URL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:147
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:142
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:38
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:53
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:50
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:52
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:48
msgid "Reset"
msgstr ""
@ -2230,7 +2230,7 @@ msgid "Staking epochs are not specified or not in the allowed range"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:25
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:24
msgid "Standard Input JSON"
msgstr ""
@ -2988,10 +2988,10 @@ msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:146
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:141
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:37
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:52
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:49
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:51
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:47
msgid "Verify & publish"
msgstr ""
@ -3152,7 +3152,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:14
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:14
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:52 lib/block_scout_web/templates/stakes/_rows.html.eex:24
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:47 lib/block_scout_web/templates/stakes/_rows.html.eex:24
msgid "Yes"
msgstr ""

@ -488,10 +488,10 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:105
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:150
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:145
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:41
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:56
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:53 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:55
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:51 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
msgstr ""
@ -611,10 +611,10 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:2
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:11
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:6
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:2
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:7
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:6 lib/block_scout_web/templates/tokens/holder/index.html.eex:15
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:6
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:4 lib/block_scout_web/templates/tokens/holder/index.html.eex:15
msgid "Connection Lost"
msgstr ""
@ -684,7 +684,7 @@ msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:91
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:86
msgid "Contract Libraries"
msgstr ""
@ -990,7 +990,7 @@ msgid "Drop sources and metadata JSON file or click here"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:29
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:28
msgid "Drop the standard input JSON file or click here"
msgstr ""
@ -1033,7 +1033,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:76
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:31
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:26
msgid "EVM Version"
msgstr ""
@ -1053,12 +1053,12 @@ msgid "Emission Reward"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:73
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:68
msgid "Enter the Solidity Contract Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:24
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:22
msgid "Enter the Vyper Contract Code"
msgstr ""
@ -1470,10 +1470,10 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:70
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:144
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:139
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:35
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:50
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:47 lib/block_scout_web/templates/address_read_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:49
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:45 lib/block_scout_web/templates/address_read_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_read_proxy/index.html.eex:12 lib/block_scout_web/templates/address_write_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_write_proxy/index.html.eex:12 lib/block_scout_web/templates/tokens/contract/index.html.eex:16
msgid "Loading..."
@ -1657,17 +1657,17 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:5
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:5
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:10
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:9
msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:14
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:9
msgid "New Solidity Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:9
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:7
msgid "New Vyper Smart Contract Verification"
msgstr ""
@ -1686,7 +1686,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:9
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:9
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:47 lib/block_scout_web/templates/stakes/_rows.html.eex:24
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:42 lib/block_scout_web/templates/stakes/_rows.html.eex:24
msgid "No"
msgstr ""
@ -1754,7 +1754,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:63
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:58
msgid "Optimization runs"
msgstr ""
@ -1975,10 +1975,10 @@ msgid "Request URL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:147
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:142
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:38
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:53
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:50
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:52
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:48
msgid "Reset"
msgstr ""
@ -2230,7 +2230,7 @@ msgid "Staking epochs are not specified or not in the allowed range"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:25
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:24
msgid "Standard Input JSON"
msgstr ""
@ -2988,10 +2988,10 @@ msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:146
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:141
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:37
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:52
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:49
#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:51
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:47
msgid "Verify & publish"
msgstr ""
@ -3152,7 +3152,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:14
#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:14
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:52 lib/block_scout_web/templates/stakes/_rows.html.eex:24
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:47 lib/block_scout_web/templates/stakes/_rows.html.eex:24
msgid "Yes"
msgstr ""

@ -12,6 +12,7 @@ defmodule Explorer.Chain.SmartContract do
use Explorer.Schema
alias Ecto.Changeset
alias Explorer.Chain.{Address, ContractMethod, DecompiledSmartContract, Hash}
alias Explorer.Chain.SmartContract.ExternalLibrary
alias Explorer.Repo
@ -200,6 +201,7 @@ defmodule Explorer.Chain.SmartContract do
* `bytecode_checked_at` - timestamp of the last check of contract's bytecode matching (DB and BlockChain)
* `contract_code_md5` - md5(`t:Explorer.Chain.Address.t/0` `contract_code`)
* `implementation_name` - name of the proxy implementation
* `autodetect_constructor_args` - field was added for storing user's choice
"""
@type t :: %Explorer.Chain.SmartContract{
@ -218,7 +220,8 @@ defmodule Explorer.Chain.SmartContract do
is_changed_bytecode: boolean,
bytecode_checked_at: DateTime.t(),
contract_code_md5: String.t(),
implementation_name: String.t() | nil
implementation_name: String.t() | nil,
autodetect_constructor_args: boolean | nil
}
schema "smart_contracts" do
@ -239,6 +242,7 @@ defmodule Explorer.Chain.SmartContract do
field(:bytecode_checked_at, :utc_datetime_usec, default: DateTime.add(DateTime.utc_now(), -86400, :second))
field(:contract_code_md5, :string)
field(:implementation_name, :string)
field(:autodetect_constructor_args, :boolean, virtual: true)
has_many(
:decompiled_smart_contracts,
@ -320,14 +324,15 @@ defmodule Explorer.Chain.SmartContract do
:is_changed_bytecode,
:bytecode_checked_at,
:contract_code_md5,
:implementation_name
:implementation_name,
:autodetect_constructor_args
])
|> (&if(json_verification,
do: &1,
else: validate_required(&1, [:name, :compiler_version, :optimization, :address_hash, :contract_code_md5])
)).()
field_to_put_message = if json_verification, do: :file, else: :contract_source_code
field_to_put_message = if json_verification, do: :file, else: select_error_field(error)
if error_message do
add_error(validated, field_to_put_message, error_message(error, error_message))
@ -375,7 +380,7 @@ defmodule Explorer.Chain.SmartContract do
end
end
defp upsert_contract_methods(%Ecto.Changeset{changes: %{abi: abi}} = changeset) do
defp upsert_contract_methods(%Changeset{changes: %{abi: abi}} = changeset) do
ContractMethod.upsert_from_abi(abi, get_field(changeset, :address_hash))
changeset
@ -396,6 +401,58 @@ defmodule Explorer.Chain.SmartContract do
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(:json), do: "Invalid JSON file."
defp error_message(:autodetect_constructor_arguments_failed),
do: "Autodetection of constructor arguments failed. Please try to input constructor arguments manually."
defp error_message(:no_creation_data),
do: "The contract creation transaction has not been indexed yet. Please wait a few minutes and try again."
defp error_message(:unknown_error), do: "Unable to verify: unknown error."
defp error_message(:deployed_bytecode), do: "Deployed bytecode does not correspond to contract creation code."
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}"
defp select_error_field(:no_creation_data), do: :address_hash
defp select_error_field(:compiler_version), do: :compiler_version
defp select_error_field(constructor_arguments)
when constructor_arguments in [:constructor_arguments, :autodetect_constructor_arguments_failed],
do: :constructor_arguments
defp select_error_field(:name), do: :name
defp select_error_field(_), do: :contract_source_code
def merge_twin_contract_with_changeset(%__MODULE__{} = twin_contract, %Changeset{} = _changeset) do
changeset(twin_contract, %{})
end
def merge_twin_contract_with_changeset(nil, %Changeset{} = changeset) do
changeset
|> Changeset.put_change(:name, "")
|> Changeset.put_change(:optimization_runs, "200")
|> Changeset.put_change(:optimization, true)
|> Changeset.put_change(:evm_version, "default")
|> Changeset.put_change(:compiler_version, "latest")
|> Changeset.put_change(:contract_source_code, "")
|> Changeset.put_change(:autodetect_constructor_args, true)
end
def merge_twin_vyper_contract_with_changeset(
%__MODULE__{is_vyper_contract: true} = twin_contract,
%Changeset{} = _changeset
) do
changeset(twin_contract, %{})
end
def merge_twin_vyper_contract_with_changeset(%__MODULE__{is_vyper_contract: false}, %Changeset{} = changeset) do
merge_twin_vyper_contract_with_changeset(nil, changeset)
end
def merge_twin_vyper_contract_with_changeset(nil, %Changeset{} = changeset) do
changeset
|> Changeset.put_change(:name, "Vyper_contract")
|> Changeset.put_change(:compiler_version, "latest")
|> Changeset.put_change(:contract_source_code, "")
end
end

@ -64,7 +64,8 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
}
],
"bytecode" => "608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820834bdab406d80509618957aa1a5ad1a4b77f4f1149078675940494ebe5b4147b0029",
"name" => "SimpleStorage"
"name" => "SimpleStorage",
"deployedBytecode" => "6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820834bdab406d80509618957aa1a5ad1a4b77f4f1149078675940494ebe5b4147b0029"
}
}
"""
@ -76,6 +77,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
optimize = Keyword.fetch!(params, :optimize)
optimization_runs = optimization_runs(params)
evm_version = Keyword.get(params, :evm_version, List.last(allowed_evm_versions()))
bytecode_hash = Keyword.get(params, :bytecode_hash, "default")
external_libs = Keyword.get(params, :external_libs, %{})
external_libs_string = Jason.encode!(external_libs)
@ -101,15 +103,19 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
optimization_runs,
@new_contract_name,
external_libs_string,
checked_evm_version
checked_evm_version,
bytecode_hash
]
)
with {:ok, decoded} <- Jason.decode(response),
{:ok, contracts} <- get_contracts(decoded),
%{"abi" => abi, "evm" => %{"bytecode" => %{"object" => bytecode}}} <-
%{
"abi" => abi,
"evm" => %{"bytecode" => %{"object" => bytecode}, "deployedBytecode" => %{"object" => deployed_bytecode}}
} <-
get_contract_info(contracts, name) do
{:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}}
{:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name, "deployedBytecode" => deployed_bytecode}}
else
{:error, %Jason.DecodeError{}} ->
{:error, :compilation}
@ -192,8 +198,18 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
defp fetch_candidates(contracts, "") when is_map(contracts) do
candidates =
for {file, content} <- contracts,
{contract_name, %{"abi" => abi, "evm" => %{"bytecode" => %{"object" => bytecode}}}} <- content,
do: %{"abi" => abi, "bytecode" => bytecode, "name" => contract_name, "file_path" => file}
{contract_name,
%{
"abi" => abi,
"evm" => %{"bytecode" => %{"object" => bytecode}, "deployedBytecode" => %{"object" => deployed_bytecode}}
}} <- content,
do: %{
"abi" => abi,
"bytecode" => bytecode,
"name" => contract_name,
"file_path" => file,
"deployedBytecode" => deployed_bytecode
}
{:ok, candidates}
end
@ -205,9 +221,19 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
else
candidates =
for {file, content} <- contracts,
{contract_name, %{"abi" => abi, "evm" => %{"bytecode" => %{"object" => bytecode}}}} <- content,
{contract_name,
%{
"abi" => abi,
"evm" => %{"bytecode" => %{"object" => bytecode}, "deployedBytecode" => %{"object" => deployed_bytecode}}
}} <- content,
contract_name == name,
do: %{"abi" => abi, "bytecode" => bytecode, "name" => contract_name, "file_path" => file}
do: %{
"abi" => abi,
"bytecode" => bytecode,
"name" => contract_name,
"file_path" => file,
"deployedBytecode" => deployed_bytecode
}
{:ok, candidates}
end
@ -216,8 +242,20 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
defp fetch_candidates(contracts, file_name, name)
when is_binary(name) and is_binary(file_name) and is_map(contracts) do
case contracts[file_name][name] do
%{"abi" => abi, "evm" => %{"bytecode" => %{"object" => bytecode}}} ->
{:ok, [%{"abi" => abi, "bytecode" => bytecode, "name" => name, "file_path" => file_name}]}
%{
"abi" => abi,
"evm" => %{"bytecode" => %{"object" => bytecode}, "deployedBytecode" => %{"object" => deployed_bytecode}}
} ->
{:ok,
[
%{
"abi" => abi,
"bytecode" => bytecode,
"name" => name,
"file_path" => file_name,
"deployedBytecode" => deployed_bytecode
}
]}
_ ->
{:ok, []}

@ -141,7 +141,8 @@ defmodule Explorer.SmartContract.Solidity.Publisher do
abi: abi,
verified_via_sourcify: params["verified_via_sourcify"],
partially_verified: params["partially_verified"],
is_vyper_contract: false
is_vyper_contract: false,
autodetect_constructor_args: params["autodetect_constructor_args"]
}
end

@ -8,17 +8,11 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
then Verified.
"""
alias ABI.{FunctionSelector, TypeDecoder}
alias Explorer.Chain
alias Explorer.SmartContract.Solidity.CodeCompiler
alias Explorer.SmartContract.Verifier.ConstructorArguments
@metadata_hash_prefix_0_4_23 "a165627a7a72305820"
@metadata_hash_prefix_0_5_family_1 "65627a7a723"
@metadata_hash_prefix_0_5_family_2 "5820"
@metadata_hash_prefix_0_6_0 "a264697066735822"
@experimental "6c6578706572696d656e74616cf5"
@metadata_hash_common_suffix "64736f6c63"
@bytecode_hash_options ["default", "none", "bzzr1"]
def evaluate_authenticity(_, %{"name" => ""}), do: {:error, :name}
@ -38,10 +32,8 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
{:ok, _} = result ->
{:cont, result}
{:error, :compiler_version} ->
{:halt, acc}
{:error, :name} ->
{:error, error}
when error in [:name, :no_creation_data, :deployed_bytecode, :compiler_version, :constructor_arguments] ->
{:halt, acc}
_ ->
@ -83,9 +75,7 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
candidate,
address_hash,
constructor_arguments,
autodetect_constructor_arguments,
source_code,
contract_name
autodetect_constructor_arguments
) do
{:ok, verified_data} ->
secondary_sources =
@ -135,68 +125,110 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
optimization_runs = Map.get(params, "optimization_runs", 200)
autodetect_constructor_arguments = params |> Map.get("autodetect_constructor_args", "false") |> parse_boolean()
solc_output =
CodeCompiler.run(
name: name,
compiler_version: compiler_version,
code: contract_source_code,
optimize: optimization,
optimization_runs: optimization_runs,
evm_version: evm_version,
external_libs: external_libraries
if is_compiler_version_at_least_0_6_0?(compiler_version) do
Enum.reduce_while(@bytecode_hash_options, false, fn option, acc ->
case acc do
{:ok, _} = result ->
{:halt, result}
{:error, error}
when error in [:name, :no_creation_data, :deployed_bytecode, :compiler_version, :constructor_arguments] ->
{:halt, acc}
_ ->
solc_output =
CodeCompiler.run(
name: name,
compiler_version: compiler_version,
code: contract_source_code,
optimize: optimization,
optimization_runs: optimization_runs,
evm_version: evm_version,
external_libs: external_libraries,
bytecode_hash: option
)
{:cont,
compare_bytecodes(
solc_output,
address_hash,
constructor_arguments,
autodetect_constructor_arguments
)}
end
end)
else
solc_output =
CodeCompiler.run(
name: name,
compiler_version: compiler_version,
code: contract_source_code,
optimize: optimization,
optimization_runs: optimization_runs,
evm_version: evm_version,
external_libs: external_libraries
)
compare_bytecodes(
solc_output,
address_hash,
constructor_arguments,
autodetect_constructor_arguments
)
end
end
defp is_compiler_version_at_least_0_6_0?(compiler_version) do
[version, _] = compiler_version |> String.split("+", parts: 2)
digits =
version
|> String.replace("v", "")
|> String.split(".")
|> Enum.map(fn str ->
{num, _} = Integer.parse(str)
num
end)
compare_bytecodes(
solc_output,
address_hash,
constructor_arguments,
autodetect_constructor_arguments,
contract_source_code,
name
)
Enum.fetch!(digits, 0) > 0 || Enum.fetch!(digits, 1) >= 6
end
defp compare_bytecodes({:error, :name}, _, _, _, _, _), do: {:error, :name}
defp compare_bytecodes({:error, _}, _, _, _, _, _), do: {:error, :compilation}
defp compare_bytecodes({:error, :name}, _, _, _), do: {:error, :name}
defp compare_bytecodes({:error, _}, _, _, _), do: {:error, :compilation}
defp compare_bytecodes({:error, _, error_message}, _, _, _, _, _) do
defp compare_bytecodes({:error, _, error_message}, _, _, _) do
{:error, :compilation, error_message}
end
defp compare_bytecodes(
%{"abi" => abi, "bytecode" => bytecode},
%{"abi" => abi, "bytecode" => bytecode, "deployedBytecode" => deployed_bytecode},
address_hash,
arguments_data,
autodetect_constructor_arguments,
contract_source_code,
contract_name
autodetect_constructor_arguments
),
do:
compare_bytecodes(
{:ok, %{"abi" => abi, "bytecode" => bytecode}},
{:ok, %{"abi" => abi, "bytecode" => bytecode, "deployedBytecode" => deployed_bytecode}},
address_hash,
arguments_data,
autodetect_constructor_arguments,
contract_source_code,
contract_name
autodetect_constructor_arguments
)
# credo:disable-for-next-line /Complexity/
defp compare_bytecodes(
{:ok, %{"abi" => abi, "bytecode" => bytecode}},
{:ok, %{"abi" => abi, "bytecode" => bytecode, "deployedBytecode" => deployed_bytecode}},
address_hash,
arguments_data,
autodetect_constructor_arguments,
contract_source_code,
contract_name
autodetect_constructor_arguments
) do
%{
"metadata_hash" => _generated_metadata_hash,
"bytecode" => generated_bytecode,
"compiler_version" => generated_compiler_version
} = extract_bytecode_and_metadata_hash(bytecode)
"metadata_hash_with_length" => local_meta,
"trimmed_bytecode" => local_bytecode_without_meta,
"compiler_version" => solc_local
} = extract_bytecode_and_metadata_hash(bytecode, deployed_bytecode)
blockchain_created_tx_input =
bc_deployed_bytecode = Chain.smart_contract_bytecode(address_hash)
bc_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
@ -207,78 +239,118 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
end
%{
"metadata_hash" => _metadata_hash,
"bytecode" => blockchain_bytecode_without_whisper,
"compiler_version" => compiler_version_from_input
} = extract_bytecode_and_metadata_hash(blockchain_created_tx_input)
"metadata_hash_with_length" => bc_meta,
"trimmed_bytecode" => bc_creation_tx_input_without_meta,
"compiler_version" => solc_bc
} = extract_bytecode_and_metadata_hash(bc_creation_tx_input, bc_deployed_bytecode)
bc_replaced_local =
String.replace(bc_creation_tx_input_without_meta, local_bytecode_without_meta, "", global: false)
has_constructor_with_params? = has_constructor_with_params?(abi)
is_constructor_args_valid? =
if has_constructor_with_params?, do: parse_constructor_and_return_check_function(abi), else: fn _ -> false end
empty_constructor_arguments = arguments_data == "" or arguments_data == nil
cond do
compiler_version_from_input != generated_compiler_version ->
bc_creation_tx_input == "" ->
{:error, :no_creation_data}
!String.contains?(bc_creation_tx_input, bc_meta) || bc_deployed_bytecode in ["", "0x"] ->
{:error, :deployed_bytecode}
solc_local != solc_bc ->
{:error, :compiler_version}
bytecode <> arguments_data == blockchain_created_tx_input ->
!String.contains?(bc_creation_tx_input_without_meta, local_bytecode_without_meta) ->
{:error, :generated_bytecode}
bc_replaced_local == "" && !has_constructor_with_params? ->
{:ok, %{abi: abi}}
bc_replaced_local != "" && has_constructor_with_params? && is_constructor_args_valid?.(bc_replaced_local) &&
autodetect_constructor_arguments ->
{:ok, %{abi: abi, constructor_arguments: bc_replaced_local}}
has_constructor_with_params? && autodetect_constructor_arguments &&
((bc_replaced_local != "" && !is_constructor_args_valid?.(bc_replaced_local)) || bc_replaced_local == "") ->
{:error, :autodetect_constructor_arguments_failed}
has_constructor_with_params? &&
(empty_constructor_arguments || !String.contains?(bc_creation_tx_input, arguments_data)) ->
{:error, :constructor_arguments}
has_constructor_with_params? && is_constructor_args_valid?.(arguments_data) &&
(bc_replaced_local == arguments_data ||
check_users_constructor_args_validity(bc_creation_tx_input, bytecode, bc_meta, local_meta, arguments_data)) ->
{:ok, %{abi: abi, constructor_arguments: arguments_data}}
generated_bytecode != blockchain_bytecode_without_whisper &&
!try_library_verification(generated_bytecode, blockchain_bytecode_without_whisper) ->
{:error, :generated_bytecode}
try_library_verification(local_bytecode_without_meta, bc_creation_tx_input_without_meta) ->
{:ok, %{abi: abi}}
has_constructor_with_params?(abi) && autodetect_constructor_arguments ->
result_1 =
try_to_verify_with_unknown_constructor_args(
blockchain_created_tx_input,
bytecode,
blockchain_bytecode_without_whisper,
abi
)
true ->
{:error, :unknown_error}
end
end
result_2 =
ConstructorArguments.find_constructor_arguments(address_hash, abi, contract_source_code, contract_name)
defp check_users_constructor_args_validity(bc_bytecode, local_bytecode, bc_splitter, local_splitter, user_arguments) do
clear_bc_bytecode = bc_bytecode |> replace_last_occurence(user_arguments) |> replace_last_occurence(bc_splitter)
clear_local_bytecode = replace_last_occurence(local_bytecode, local_splitter)
clear_bc_bytecode == clear_local_bytecode
end
cond do
result_1 ->
{:ok, %{abi: abi, constructor_arguments: result_1}}
defp replace_last_occurence(where, what) when is_binary(where) and is_binary(what) do
where
|> String.reverse()
|> String.replace(String.reverse(what), "", global: false)
|> String.reverse()
end
result_2 ->
{:ok, %{abi: abi, constructor_arguments: result_2}}
defp replace_last_occurence(_, _), do: nil
true ->
{:error, :constructor_arguments}
end
defp parse_constructor_and_return_check_function(abi) do
constructor_abi = Enum.find(abi, fn el -> el["type"] == "constructor" && el["inputs"] != [] end)
has_constructor_with_params?(abi) && empty_constructor_arguments ->
{:error, :constructor_arguments}
input_types = Enum.map(constructor_abi["inputs"], &FunctionSelector.parse_specification_type/1)
has_constructor_with_params?(abi) &&
!ConstructorArguments.verify(
address_hash,
blockchain_bytecode_without_whisper,
arguments_data,
contract_source_code,
contract_name
) ->
if try_to_verify_with_unknown_constructor_args(
blockchain_created_tx_input,
bytecode,
blockchain_bytecode_without_whisper,
abi
) == arguments_data |> String.trim_trailing() |> String.trim_leading("0x") do
{:ok, %{abi: abi}}
else
{:error, :constructor_arguments}
end
fn assumed_arguments ->
try do
_ =
assumed_arguments
|> Base.decode16!(case: :mixed)
|> TypeDecoder.decode_raw(input_types)
true ->
{:ok, %{abi: abi}}
assumed_arguments
rescue
_ ->
false
end
end
end
defp try_to_verify_with_unknown_constructor_args(creation_code, generated_bytecode, trimmed_bytecode, abi) do
["", rest_blockchain] = String.split(creation_code, trimmed_bytecode)
["", rest_generated] = String.split(generated_bytecode, trimmed_bytecode)
ConstructorArguments.experimental_find_constructor_args(rest_blockchain, rest_generated, abi)
defp extract_meta_from_deployed_bytecode(code_unknown_case) do
with true <- is_binary(code_unknown_case),
code <- String.downcase(code_unknown_case),
last_2_bytes <- code |> String.slice(-4..-1),
{meta_length, ""} <- last_2_bytes |> Integer.parse(16),
meta <- String.slice(code, (-(meta_length + 2) * 2)..-5) do
{meta, last_2_bytes}
else
_ ->
{"", ""}
end
end
defp decode_meta(meta) do
with {:ok, meta_raw_binary} <- Base.decode16(meta, case: :lower),
{:ok, decoded_meta, _remain} <- CBOR.decode(meta_raw_binary) do
decoded_meta
else
_ ->
%{}
end
end
# 730000000000000000000000000000000000000000 - default library address that returned by the compiler
@ -300,163 +372,24 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
For more information on the swarm hash, check out:
https://solidity.readthedocs.io/en/v0.5.3/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
"""
def extract_bytecode_and_metadata_hash(nil) do
%{"metadata_hash" => nil, "bytecode" => nil, "compiler_version" => nil}
def extract_bytecode_and_metadata_hash("0x" <> bytecode, deployed_bytecode) do
extract_bytecode_and_metadata_hash(bytecode, deployed_bytecode)
end
def extract_bytecode_and_metadata_hash("0x" <> code) do
%{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version} =
extract_bytecode_and_metadata_hash(code)
def extract_bytecode_and_metadata_hash(bytecode, deployed_bytecode) do
{meta, meta_length} = extract_meta_from_deployed_bytecode(deployed_bytecode)
%{"metadata_hash" => metadata_hash, "bytecode" => "0x" <> bytecode, "compiler_version" => compiler_version}
end
solc = decode_meta(meta)["solc"]
def extract_bytecode_and_metadata_hash(code) do
do_extract_bytecode_and_metadata_hash([], String.downcase(code), nil, nil)
end
defp do_extract_bytecode_and_metadata_hash(extracted, remaining, metadata_hash, compiler_version) do
case remaining do
<<>> ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
@metadata_hash_prefix_0_4_23 <> <<metadata_hash::binary-size(64)>> <> "0029" <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
# Solidity >= 0.5 family && experimantal
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<metadata_hash::binary-size(64)>> <>
@experimental <>
@metadata_hash_common_suffix <>
"43" <> <<compiler_version::binary-size(6)>> <> <<_::binary-size(4)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<metadata_hash::binary-size(64)>> <>
@experimental <>
<<_::binary-size(4)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
# Solidity >= 0.5.9; https://github.com/ethereum/solidity/blob/aa4ee3a1559ebc0354926af962efb3fcc7dc15bd/docs/metadata.rst
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<metadata_hash::binary-size(64)>> <>
@metadata_hash_common_suffix <>
"43" <> <<compiler_version::binary-size(6)>> <> "0032" <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<metadata_hash::binary-size(64)>> <>
@metadata_hash_common_suffix <>
"78" <>
<<_::binary-size(2)>> <>
<<compiler_version::binary-size(76)>> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<metadata_hash::binary-size(64)>> <>
@metadata_hash_common_suffix <>
"78" <>
<<_::binary-size(2)>> <>
<<compiler_version::binary-size(78)>> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<metadata_hash::binary-size(64)>> <>
@metadata_hash_common_suffix <>
"78" <>
<<_::binary-size(2)>> <>
<<compiler_version::binary-size(80)>> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<metadata_hash::binary-size(64)>> <>
@metadata_hash_common_suffix <>
"78" <>
<<_::binary-size(2)>> <>
<<compiler_version::binary-size(82)>> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
# Solidity >= 0.6.0 https://github.com/ethereum/solidity/blob/develop/Changelog.md#060-2019-12-17
# https://github.com/ethereum/solidity/blob/26b700771e9cc9c956f0503a05de69a1be427963/docs/metadata.rst#encoding-of-the-metadata-hash-in-the-bytecode
# IPFS is used instead of Swarm
# The current version of the Solidity compiler usually adds the following to the end of the deployed bytecode:
# 0xa2
# 0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash>
# 0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding>
# 0x00 0x32
# Note: there is a bug in the docs. Instead of 0x32, 0x33 should be used.
# Fixing PR has been created https://github.com/ethereum/solidity/pull/8174
@metadata_hash_prefix_0_6_0 <>
<<metadata_hash::binary-size(68)>> <>
@metadata_hash_common_suffix <>
"43" <> <<compiler_version::binary-size(6)>> <> "0033" <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
@metadata_hash_prefix_0_6_0 <>
<<metadata_hash::binary-size(68)>> <>
@metadata_hash_common_suffix <>
"78" <>
<<_::binary-size(2)>> <>
<<compiler_version::binary-size(76)>> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
@metadata_hash_prefix_0_6_0 <>
<<metadata_hash::binary-size(68)>> <>
@metadata_hash_common_suffix <>
"78" <>
<<_::binary-size(2)>> <>
<<compiler_version::binary-size(78)>> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
@metadata_hash_prefix_0_6_0 <>
<<metadata_hash::binary-size(68)>> <>
@metadata_hash_common_suffix <>
"78" <>
<<_::binary-size(2)>> <>
<<compiler_version::binary-size(80)>> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
@metadata_hash_prefix_0_6_0 <>
<<metadata_hash::binary-size(68)>> <>
@metadata_hash_common_suffix <>
"78" <>
<<_::binary-size(2)>> <>
<<compiler_version::binary-size(82)>> <> "00" <> <<_::binary-size(2)>> <> _constructor_arguments ->
do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version)
<<next::binary-size(2)>> <> rest ->
do_extract_bytecode_and_metadata_hash([next | extracted], rest, metadata_hash, compiler_version)
end
end
defp do_extract_bytecode_and_metadata_hash_output(metadata_hash, extracted, compiler_version) do
bytecode =
extracted
|> Enum.reverse()
|> :binary.list_to_bin()
bytecode_without_meta =
bytecode
|> replace_last_occurence(meta <> meta_length)
%{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version}
%{
"metadata_hash_with_length" => meta <> meta_length,
"trimmed_bytecode" => bytecode_without_meta,
"compiler_version" => solc
}
end
def previous_evm_versions(current_evm_version) do

@ -1,525 +0,0 @@
# credo:disable-for-this-file
defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
@moduledoc """
Smart contract contrstructor arguments verification logic.
"""
alias ABI.{FunctionSelector, TypeDecoder}
alias Explorer.Chain
@metadata_hash_prefix_0_4_23 "a165627a7a72305820"
@metadata_hash_prefix_0_5_family_1 "65627a7a723"
@metadata_hash_prefix_0_5_family_2 "5820"
@metadata_hash_prefix_0_6_0 "a264697066735822"
@experimental "6c6578706572696d656e74616cf5"
@metadata_hash_common_suffix "64736f6c63"
def verify(address_hash, contract_code, arguments_data, contract_source_code, contract_name) do
arguments_data = arguments_data |> String.trim_trailing() |> String.trim_leading("0x")
creation_code =
address_hash
|> Chain.contract_creation_input_data()
|> String.replace("0x", "")
check_func = fn assumed_arguments -> assumed_arguments == arguments_data end
if verify_older_version(creation_code, contract_code, check_func) do
true
else
extract_constructor_arguments(creation_code, check_func, contract_source_code, contract_name)
end
end
# Earlier versions of Solidity didn't have whisper code.
# constructor argument were directly appended to source code
defp verify_older_version(creation_code, contract_code, check_func) do
creation_code
|> String.split(contract_code)
|> List.last()
|> check_func.()
end
defp extract_constructor_arguments(code, check_func, contract_source_code, contract_name) do
case code do
# Solidity ~ 4.23 # https://solidity.readthedocs.io/en/v0.4.23/metadata.html
@metadata_hash_prefix_0_4_23 <> <<_::binary-size(64)>> <> "0029" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_4_23
)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<_::binary-size(64)>> <>
@experimental <>
@metadata_hash_common_suffix <>
"43" <> <<_::binary-size(6)>> <> <<_::binary-size(4)>> <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_5_family_1
)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<_::binary-size(64)>> <>
@experimental <>
<<_::binary-size(4)>> <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_5_family_1
)
# Solidity >= 0.5.10 https://solidity.readthedocs.io/en/v0.5.10/metadata.html
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<_::binary-size(64)>> <>
@metadata_hash_common_suffix <> "43" <> <<_::binary-size(6)>> <> "0032" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_5_family_1
)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<_::binary-size(64)>> <>
@metadata_hash_common_suffix <> "7826" <> <<_::binary-size(76)>> <> "0057" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_5_family_1
)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<_::binary-size(64)>> <>
@metadata_hash_common_suffix <> "7827" <> <<_::binary-size(78)>> <> "0057" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_5_family_1
)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<_::binary-size(64)>> <>
@metadata_hash_common_suffix <> "7828" <> <<_::binary-size(80)>> <> "0058" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_5_family_1
)
<<_::binary-size(2)>> <>
@metadata_hash_prefix_0_5_family_1 <>
<<_::binary-size(1)>> <>
@metadata_hash_prefix_0_5_family_2 <>
<<_::binary-size(64)>> <>
@metadata_hash_common_suffix <> "7829" <> <<_::binary-size(82)>> <> "0059" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_5_family_1
)
# Solidity >= 0.6.0 https://github.com/ethereum/solidity/blob/develop/Changelog.md#060-2019-12-17
# https://github.com/ethereum/solidity/blob/26b700771e9cc9c956f0503a05de69a1be427963/docs/metadata.rst#encoding-of-the-metadata-hash-in-the-bytecode
# IPFS is used instead of Swarm
# The current version of the Solidity compiler usually adds the following to the end of the deployed bytecode:
# 0xa2
# 0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash>
# 0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding>
# 0x00 0x32
# Note: there is a bug in the docs. Instead of 0x32, 0x33 should be used.
# Fixing PR has been created https://github.com/ethereum/solidity/pull/8174
@metadata_hash_prefix_0_6_0 <>
<<_::binary-size(68)>> <>
@metadata_hash_common_suffix <> "43" <> <<_::binary-size(6)>> <> "0033" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_6_0
)
@metadata_hash_prefix_0_6_0 <>
<<_::binary-size(68)>> <>
@metadata_hash_common_suffix <> "7826" <> <<_::binary-size(76)>> <> "0057" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_6_0
)
@metadata_hash_prefix_0_6_0 <>
<<_::binary-size(68)>> <>
@metadata_hash_common_suffix <> "7827" <> <<_::binary-size(78)>> <> "0057" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_6_0
)
@metadata_hash_prefix_0_6_0 <>
<<_::binary-size(68)>> <>
@metadata_hash_common_suffix <> "7828" <> <<_::binary-size(80)>> <> "0058" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_6_0
)
@metadata_hash_prefix_0_6_0 <>
<<_::binary-size(68)>> <>
@metadata_hash_common_suffix <> "7829" <> <<_::binary-size(82)>> <> "0059" <> constructor_arguments ->
split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
@metadata_hash_prefix_0_6_0
)
<<>> ->
false
<<_::binary-size(2)>> <> rest ->
extract_constructor_arguments(rest, check_func, contract_source_code, contract_name)
end
end
defp is_constructor_arguments_still_has_metadata_prefix(constructor_arguments, prefix) do
constructor_arguments_parts =
constructor_arguments
|> String.split(prefix)
|> Enum.count()
constructor_arguments_parts > 1
end
defp split_constructor_arguments_and_extract_check_func(constructor_arguments, nil, nil, nil, _metadata_hash_prefix),
do: constructor_arguments
defp split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name,
metadata_hash_prefix
) do
if is_constructor_arguments_still_has_metadata_prefix(constructor_arguments, metadata_hash_prefix) do
{ind, _} = :binary.match(constructor_arguments, metadata_hash_prefix)
offset = if ind > 2, do: ind - 2, else: 0
processed_constructor_arguments =
if offset > 0 do
<<_::binary-size(offset)>> <> rest = constructor_arguments
rest
else
constructor_arguments
end
extract_constructor_arguments(processed_constructor_arguments, check_func, contract_source_code, contract_name)
else
extract_constructor_arguments_check_func(
constructor_arguments,
check_func,
contract_source_code,
contract_name
)
end
end
defp extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name) do
check_func_result_before_removing_strings = check_func.(constructor_arguments)
constructor_arguments =
remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name)
filtered_constructor_arguments =
remove_keccak256_strings_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name)
check_func_result = check_func.(filtered_constructor_arguments)
cond do
check_func_result_before_removing_strings ->
check_func_result_before_removing_strings
check_func_result ->
check_func_result
true ->
output =
extract_constructor_arguments(filtered_constructor_arguments, check_func, contract_source_code, contract_name)
if output do
output
else
# https://github.com/blockscout/blockscout/pull/4764
clean_constructor_arguments =
remove_substring_of_require_messages(filtered_constructor_arguments, contract_source_code, contract_name)
check_func.(clean_constructor_arguments)
end
end
end
defp remove_substring_of_require_messages(constructor_arguments, contract_source_code, contract_name) do
require_msgs =
contract_source_code
|> extract_require_messages_from_constructor(contract_name)
|> Enum.filter(fn require_msg -> require_msg != nil end)
longest_substring_to_delete =
Enum.reduce(require_msgs, "", fn msg, best_match ->
case String.myers_difference(constructor_arguments, msg) do
[{:eq, match} | _] ->
if String.length(match) > String.length(best_match) do
match
else
best_match
end
_ ->
best_match
end
end)
[_, cleaned_constructor_arguments] = String.split(constructor_arguments, longest_substring_to_delete, parts: 2)
cleaned_constructor_arguments
end
def remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name) do
all_msgs =
contract_source_code
|> extract_require_messages_from_constructor(contract_name)
filtered_msgs =
all_msgs
|> Enum.filter(fn require_msg -> require_msg != nil end)
msgs_list =
filtered_msgs
|> Enum.reverse()
Enum.reduce(msgs_list, constructor_arguments, fn msg, pure_constructor_arguments ->
case String.split(pure_constructor_arguments, msg, parts: 2) do
[_, constructor_arguments_part] ->
constructor_arguments_part
[_] ->
pure_constructor_arguments
end
end)
end
def remove_keccak256_strings_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name) do
all_strings =
contract_source_code
|> extract_strings_from_constructor(contract_name)
strings_list =
all_strings
|> Enum.reverse()
Enum.reduce(strings_list, constructor_arguments, fn msg, pure_constructor_arguments ->
case String.split(pure_constructor_arguments, msg, parts: 2) do
[_, constructor_arguments_part] ->
constructor_arguments_part
[_] ->
pure_constructor_arguments
end
end)
end
def find_constructor_arguments(address_hash, abi, contract_source_code, contract_name) do
creation_code =
address_hash
|> Chain.contract_creation_input_data()
|> String.replace("0x", "")
check_func = parse_constructor_and_return_check_func(abi)
extract_constructor_arguments(creation_code, check_func, contract_source_code, contract_name)
end
defp parse_constructor_and_return_check_func(abi) do
constructor_abi = Enum.find(abi, fn el -> el["type"] == "constructor" && el["inputs"] != [] end)
input_types = Enum.map(constructor_abi["inputs"], &FunctionSelector.parse_specification_type/1)
fn assumed_arguments ->
try do
_ =
assumed_arguments
|> Base.decode16!(case: :mixed)
|> TypeDecoder.decode_raw(input_types)
assumed_arguments
rescue
_ ->
false
end
end
end
def extract_require_messages_from_constructor(contract_source_code, _contract_name) do
# todo: _contract_name is for parsing of actually used constructor for concrete contract
require_contents = find_all_requires(contract_source_code)
messages_list =
Enum.reduce(require_contents, [], fn require_content, msgs ->
msg = get_require_message_hex(require_content)
if msg, do: [msg | msgs], else: msgs
end)
if messages_list, do: messages_list, else: []
end
def extract_strings_from_constructor(contract_source_code, _contract_name) do
keccak256_contents = find_all_strings(contract_source_code)
strings_list =
Enum.reduce(keccak256_contents, [], fn keccak256_content, strs ->
str = get_keccak256_string_hex(keccak256_content)
if str, do: [str | strs], else: strs
end)
if strings_list, do: strings_list, else: []
end
def find_constructor_content(contract_source_code) do
case String.split(contract_source_code, "constructor", parts: 2) do
[_, right_from_contstructor] ->
[_, right_from_contstructor_inside] = String.split(right_from_contstructor, "{", parts: 2)
[constructor, _] = String.split(right_from_contstructor_inside, "}", parts: 2)
constructor
[_] ->
nil
end
end
def find_all_requires(contract_source_code) do
if contract_source_code do
trimmed_source_code =
contract_source_code
|> String.replace(~r/ +/, " ")
|> String.replace("require(", "require (")
[_ | requires] =
trimmed_source_code
|> String.split("require (")
Enum.reduce(requires, [], fn right_from_require, requires_list ->
[require_content | _] = String.split(right_from_require, ");", parts: 2)
[require_content | requires_list]
end)
else
[]
end
end
def find_all_strings(contract_source_code) do
if contract_source_code do
[_ | keccak256s] = String.split(contract_source_code, "keccak256")
Enum.reduce(keccak256s, [], fn right_from_keccak256, keccak256s_list ->
parts = String.split(right_from_keccak256, "\"")
if Enum.count(parts) >= 3 do
[_ | [right_from_keccak256_inside]] = String.split(right_from_keccak256, "\"", parts: 2)
[keccak256_content | _] = String.split(right_from_keccak256_inside, "\"", parts: 2)
[keccak256_content | keccak256s_list]
else
keccak256s_list
end
end)
else
[]
end
end
def get_require_message_hex(require_content) do
parts = String.split(require_content, ",")
if Enum.count(parts) > 1 do
[msg] = Enum.take(parts, -1)
msg
|> String.trim()
|> String.trim_leading("\"")
|> String.trim_trailing("\"")
|> String.trim_leading("'")
|> String.trim_trailing("'")
|> Base.encode16(case: :lower)
else
nil
end
end
def get_keccak256_string_hex(keccak256_content) do
if keccak256_content !== "" do
keccak256_content
|> String.trim()
|> String.trim_leading("\"")
|> String.trim_trailing("\"")
|> String.trim_leading("'")
|> String.trim_trailing("'")
|> Base.encode16(case: :lower)
else
nil
end
end
def experimental_find_constructor_args(rest_creation, rest_generated, abi) do
got_args =
rest_creation
|> String.split(extract_constructor_arguments(rest_generated, nil, nil, nil))
|> List.last()
abi
|> parse_constructor_and_return_check_func()
|> (& &1.(got_args)).()
end
end

@ -109,7 +109,8 @@ defmodule Explorer.Mixfile do
# `Timex.Duration` for `Explorer.Counters.AverageBlockTime.average_block_time/0`
{:timex, "~> 3.7.1"},
{:con_cache, "~> 1.0"},
{:tesla, "~> 1.3.3"}
{:tesla, "~> 1.3.3"},
{:cbor, "~> 1.0"}
]
end

@ -7,6 +7,7 @@ var optimizationRuns = parseInt(process.argv[5], 10);
var newContractName = process.argv[6];
var externalLibraries = JSON.parse(process.argv[7])
var evmVersion = process.argv[8];
var bytecodeHash = process.argv[9];
var solc = require('solc')
var compilerSnapshot = require(compilerVersionPath);
@ -34,6 +35,10 @@ if (evmVersion !== 'default') {
settings = Object.assign(settings, {evmVersion: evmVersion})
}
if (bytecodeHash !== 'default') {
settings = Object.assign(settings, {metadata: {bytecodeHash: bytecodeHash}})
}
const input = {
language: 'Solidity',
sources: {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -2,10 +2,10 @@
{
"compiler_version": "v0.5.11+commit.c082d0b4",
"contract": "pragma solidity 0.5.11;library BadSafeMath { function add(uint256 a, uint256 b) public pure returns (uint256) { uint256 c = a + 2 * b; require(c >= a, \"SafeMath: addition overflow\"); return c; }}contract SimpleStorage { uint256 storedData = 10; using BadSafeMath for uint256; function increment(uint256 x) public { storedData = storedData.add(x); } function set(uint256 x) public { storedData = x; } function get() public view returns (uint256) { return storedData; }}",
"expected_bytecode": "608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054733662e222908fa35f013bee37695d0510098b6d7363771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a723158203e59bfb9a5a2e55d38231922c86d8b2ec9b66cb2f6595613674bc4e15290b60764736f6c634300050b0032",
"tx_input": "6080604052600a60005534801561001557600080fd5b50610169806100256000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054733662e222908fa35f013bee37695d0510098b6d7363771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a723158207809bc828bbcd3de3e6b6483facb0c5902901fc8827283c749c8ea0702eb871f64736f6c634300050b0032",
"expected_bytecode": "608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054739bca1bf2810c9b68f25c82e8ebb9dc0a5301e31063771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a72315820d232994a5def56e2405afb76ba2bdc218997c0083fa707a949d0465c39d9f27964736f6c634300050b0032",
"tx_input": "6080604052600a60005534801561001557600080fd5b50610169806100256000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806360fe47b1146100465780636d4ce63c146100655780637cf5dab01461007f575b600080fd5b6100636004803603602081101561005c57600080fd5b503561009c565b005b61006d6100a1565b60408051918252519081900360200190f35b6100636004803603602081101561009557600080fd5b50356100a7565b600055565b60005490565b600054739bca1bf2810c9b68f25c82e8ebb9dc0a5301e31063771602f79091836040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561010257600080fd5b505af4158015610116573d6000803e3d6000fd5b505050506040513d602081101561012c57600080fd5b50516000555056fea265627a7a72315820d232994a5def56e2405afb76ba2bdc218997c0083fa707a949d0465c39d9f27964736f6c634300050b0032",
"external_libraries": {
"BadSafeMath": "0x3662e222908fa35f013bee37695d0510098b6d73"
"BadSafeMath": "0x9Bca1BF2810c9b68F25c82e8eBb9dC0A5301e310"
},
"name": "SimpleStorage",
"optimize": true

@ -0,0 +1,259 @@
pragma solidity ^0.5.8;
contract ValidatorProxy {
mapping(address => bool) public isValidator;
address public systemAddress = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE;
address[] public validators;
constructor(address[] memory _validators) public {
validators = _validators;
for (uint i = 0; i < _validators.length; i++) {
isValidator[_validators[i]] = true;
}
}
function updateValidators(address[] memory newValidators) public {
require(
tx.origin == systemAddress, // solium-disable-line security/no-tx-origin
"Only the system address can be responsible for the call of this function."
);
for (uint i = 0; i < validators.length; i++) {
isValidator[validators[i]] = false;
}
for (uint i = 0; i < newValidators.length; i++) {
isValidator[newValidators[i]] = true;
}
validators = newValidators;
}
function numberOfValidators() public view returns (uint) {
return validators.length;
}
function getValidators() public view returns (address[] memory) {
return validators;
}
}
contract HomeBridge {
struct TransferState {
mapping(address => bool) isConfirmedByValidator;
address[] confirmingValidators;
bool isCompleted;
}
event Confirmation(
bytes32 transferHash,
bytes32 transactionHash,
uint256 amount,
address recipient,
address indexed validator
);
event TransferCompleted(
bytes32 transferHash,
bytes32 transactionHash,
uint256 amount,
address recipient,
bool coinTransferSuccessful
);
mapping(bytes32 => TransferState) public transferState;
ValidatorProxy public validatorProxy;
uint public validatorsRequiredPercent;
constructor(ValidatorProxy _proxy, uint _validatorsRequiredPercent) public {
require(
address(_proxy) != address(0),
"proxy must not be the zero address!"
);
require(
_validatorsRequiredPercent >= 0 &&
_validatorsRequiredPercent <= 100,
"_validatorsRequiredPercent must be between 0 and 100"
);
validatorProxy = _proxy;
validatorsRequiredPercent = _validatorsRequiredPercent;
}
function fund() external payable {}
function confirmTransfer(
bytes32 transferHash,
bytes32 transactionHash,
uint256 amount,
address payable recipient
) public {
// We compute a keccak hash for the transfer and use that as an identifier for the transfer
bytes32 transferStateId = keccak256(
abi.encodePacked(transferHash, transactionHash, amount, recipient)
);
require(
!transferState[transferStateId].isCompleted,
"transfer already completed"
);
require(
validatorProxy.isValidator(msg.sender),
"must be validator to confirm transfers"
);
require(
recipient != address(0),
"recipient must not be the zero address!"
);
require(amount > 0, "amount must not be zero");
if (_confirmTransfer(transferStateId, msg.sender)) {
// We have to emit the events here, because _confirmTransfer
// doesn't even receive the necessary information to do it on
// its own
emit Confirmation(
transferHash,
transactionHash,
amount,
recipient,
msg.sender
);
}
if (_requiredConfirmationsReached(transferStateId)) {
transferState[transferStateId].isCompleted = true;
delete transferState[transferStateId].confirmingValidators;
bool coinTransferSuccessful = recipient.send(amount);
emit TransferCompleted(
transferHash,
transactionHash,
amount,
recipient,
coinTransferSuccessful
);
}
}
// check if a 2nd confirmTransfer would complete a transfer. this
// can happen after validator set changes.
function reconfirmCompletesTransfer(
bytes32 transferHash,
bytes32 transactionHash,
uint256 amount,
address payable recipient
) public view returns (bool) {
require(
recipient != address(0),
"recipient must not be the zero address!"
);
require(amount > 0, "amount must not be zero");
// We compute a keccak hash for the transfer and use that as an identifier for the transfer
bytes32 transferStateId = keccak256(
abi.encodePacked(transferHash, transactionHash, amount, recipient)
);
require(
!transferState[transferStateId].isCompleted,
"transfer already completed"
);
address[] storage confirmingValidators = transferState[transferStateId]
.confirmingValidators;
uint numConfirming = 0;
for (uint i = 0; i < confirmingValidators.length; i++) {
if (validatorProxy.isValidator(confirmingValidators[i])) {
numConfirming += 1;
}
}
return numConfirming >= _getNumRequiredConfirmations();
}
function _purgeConfirmationsFromExValidators(bytes32 transferStateId)
internal
{
address[] storage confirmingValidators = transferState[transferStateId]
.confirmingValidators;
uint i = 0;
while (i < confirmingValidators.length) {
if (validatorProxy.isValidator(confirmingValidators[i])) {
i++;
} else {
confirmingValidators[i] = confirmingValidators[confirmingValidators
.length -
1];
confirmingValidators.length--;
}
}
}
function _getNumRequiredConfirmations() internal view returns (uint) {
return
(
validatorProxy.numberOfValidators() *
validatorsRequiredPercent +
99
) /
100;
}
function _confirmTransfer(bytes32 transferStateId, address validator)
internal
returns (bool)
{
if (transferState[transferStateId].isConfirmedByValidator[validator]) {
return false;
}
transferState[transferStateId].isConfirmedByValidator[validator] = true;
transferState[transferStateId].confirmingValidators.push(validator);
return true;
}
function _requiredConfirmationsReached(bytes32 transferStateId)
internal
returns (bool)
{
uint numRequired = _getNumRequiredConfirmations();
/* We now check if we have enough confirmations. If that is the
case, we purge ex-validators from the list of confirmations
and do the check again, so we do not count
confirmations from ex-validators.
This means that old confirmations stay valid over validator set changes given
that the validator doesn't lose its validator status.
The double check is here to save some gas. If checking the validator
status for all confirming validators becomes too costly, we can introduce
a 'serial number' for the validator set changes and determine if there
was a change of the validator set between the first confirmation
and the last confirmation and skip calling into
_purgeConfirmationsFromExValidators if there were no changes.
*/
if (
transferState[transferStateId].confirmingValidators.length <
numRequired
) {
return false;
}
_purgeConfirmationsFromExValidators(transferStateId);
if (
transferState[transferStateId].confirmingValidators.length <
numRequired
) {
return false;
}
return true;
}
}

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Owner {
address private owner;
event OwnerSet(address indexed oldOwner, address indexed newOwner);
modifier isOwner() {
require(msg.sender == owner, "Caller is not owner");
_;
}
constructor(int a1, uint256 a2) {
owner = msg.sender;
emit OwnerSet(address(0), owner);
}
function changeOwner(address newOwner) public isOwner {
emit OwnerSet(owner, newOwner);
owner = newOwner;
}
function getOwner() external view returns (address) {
return owner;
}
}

@ -11,6 +11,7 @@
"briefly": {:git, "https://github.com/CargoSense/briefly.git", "25942fba9cad46aaa870ba248c101ffee321ec9b", []},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"bypass": {:hex, :bypass, "1.0.0", "b78b3dcb832a71aca5259c1a704b2e14b55fd4e1327ff942598b4e7d1a7ad83d", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}], "hexpm", "5a1dc855dfcc86160458c7a70d25f65d498bd8012bd4c06a8d3baa368dda3c45"},
"cbor": {:hex, :cbor, "1.0.0", "35d33a26f6420ce3d2d01c0b1463a748b34c537d5609fc40116daf3666700d36", [:mix], [], "hexpm", "cc5e21e0fa5a0330715a3806c67bc294f8b65d07160f751b5bd6058bed1962ac"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"cldr_utils": {:hex, :cldr_utils, "2.14.0", "edcef8dd2654b93d84a90087f3536ffabf9c9d82b34ff82bc9ca54c0668b3a4a", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "6903356ff6988342a29b90637eece4ca98a4ed2b9759c22233d3474ade57645a"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},

Loading…
Cancel
Save