Add VerificationController to API v2

pull/6744/head
Никита Поздняков 2 years ago committed by Nikita Pozdniakov
parent 8464e8254a
commit d72bd5a1a1
No known key found for this signature in database
GPG Key ID: F344106F9804FE5F
  1. 3
      .dialyzer-ignore
  2. 0
      apps/block_scout_web/lib/block_scout_web/api_v2.ex
  3. 21
      apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex
  4. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex
  5. 167
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
  6. 7
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex
  7. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_json_controller.ex
  8. 7
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex
  9. 59
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
  10. 24
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex
  11. 4
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex
  12. 178
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex
  13. 12
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  14. 10
      apps/block_scout_web/lib/block_scout_web/smart_contracts_api_v2_router.ex
  15. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex
  16. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex
  17. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex
  18. 9
      apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex
  19. 2
      apps/explorer/lib/explorer/chain/smart_contract.ex
  20. 24
      apps/explorer/lib/explorer/smart_contract/compiler_version.ex
  21. 2
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  22. 217
      apps/explorer/lib/explorer/smart_contract/solidity/publish_helper.ex
  23. 4
      apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
  24. 53
      apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex
  25. 55
      apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
  26. 3
      apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
  27. 13
      apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex
  28. 114
      apps/explorer/lib/explorer/third_party_integrations/sourcify.ex
  29. 3
      apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs

@ -22,11 +22,8 @@ lib/explorer/smart_contract/reader.ex:435
lib/indexer/fetcher/token_total_supply_on_demand.ex:16
lib/explorer/exchange_rates/source.ex:116
lib/explorer/exchange_rates/source.ex:119
lib/explorer/smart_contract/solidity/verifier.ex:318
lib/block_scout_web/templates/address_contract/index.html.eex:158
lib/block_scout_web/templates/address_contract/index.html.eex:195
lib/explorer/third_party_integrations/sourcify.ex:121
lib/explorer/third_party_integrations/sourcify.ex:124
lib/block_scout_web/views/transaction_view.ex:137
lib/block_scout_web/views/transaction_view.ex:152
lib/block_scout_web/views/transaction_view.ex:197

@ -5,6 +5,7 @@ defmodule BlockScoutWeb.AddressChannel do
use BlockScoutWeb, :channel
alias BlockScoutWeb.API.V2.AddressView, as: AddressViewAPI
alias BlockScoutWeb.API.V2.SmartContractView, as: SmartContractViewAPI
alias BlockScoutWeb.API.V2.TransactionView, as: TransactionViewAPI
alias BlockScoutWeb.{
@ -93,6 +94,26 @@ defmodule BlockScoutWeb.AddressChannel do
end
end
def handle_out(
"verification_result",
%{result: result},
%Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket
) do
case result do
{:ok, _contract} ->
push(socket, "verification_result", %{status: "success"})
{:noreply, socket}
{:error, changeset} ->
push(socket, "verification_result", %{
status: "error",
errors: SmartContractViewAPI.render("changeset_errors.json", %{changeset: changeset})
})
{:noreply, socket}
end
end
def handle_out("verification_result", result, socket) do
case result[:result] do
{:ok, _contract} ->

@ -6,9 +6,9 @@ defmodule BlockScoutWeb.AddressContractController do
import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2]
alias BlockScoutWeb.AccessHelpers
alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Explorer.SmartContract.Solidity.PublishHelper
alias Indexer.Fetcher.CoinBalanceOnDemand
def index(conn, %{"address_id" => address_hash_string} = params) do
@ -24,7 +24,7 @@ defmodule BlockScoutWeb.AddressContractController do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
_ <- VerificationController.check_and_verify(address_hash_string),
_ <- PublishHelper.check_and_verify(address_hash_string),
{:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do
render(
conn,

@ -1,14 +1,13 @@
defmodule BlockScoutWeb.AddressContractVerificationController do
use BlockScoutWeb, :controller
alias BlockScoutWeb.API.RPC.ContractController
alias BlockScoutWeb.Controller
alias Ecto.Changeset
alias Explorer.Chain
alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.SmartContract
alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler}
alias Explorer.SmartContract.Solidity.PublisherWorker, as: SolidityPublisherWorker
alias Explorer.SmartContract.Solidity.PublishHelper
alias Explorer.SmartContract.Vyper.PublisherWorker, as: VyperPublisherWorker
alias Explorer.ThirdPartyIntegrations.Sourcify
@ -57,7 +56,7 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
files_array =
files
|> Map.values()
|> read_files()
|> PublishHelper.read_files()
Que.add(SolidityPublisherWorker, {"multipart", smart_contract, files_array, external_libraries, conn})
@ -85,9 +84,9 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
"verification_type" => "json:standard"
}
) do
files_array = prepare_files_array(files)
files_array = PublishHelper.prepare_files_array(files)
with %Plug.Upload{path: path} <- get_one_json(files_array),
with %Plug.Upload{path: path} <- PublishHelper.get_one_json(files_array),
{:ok, json_input} <- File.read(path) do
Que.add(SolidityPublisherWorker, {"json_web", smart_contract, json_input, conn})
else
@ -118,14 +117,14 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
"verification_type" => "json:metadata"
}
) do
files_array = prepare_files_array(files)
files_array = PublishHelper.prepare_files_array(files)
json_file = get_one_json(files_array)
json_file = PublishHelper.get_one_json(files_array)
if json_file do
if Chain.smart_contract_fully_verified?(address_hash_string) do
EventsPublisher.broadcast(
prepare_verification_error(
PublishHelper.prepare_verification_error(
"This contract already verified in Blockscout.",
address_hash_string,
conn
@ -135,15 +134,15 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
else
case Sourcify.check_by_address(address_hash_string) do
{:ok, _verified_status} ->
get_metadata_and_publish(address_hash_string, conn)
PublishHelper.get_metadata_and_publish(address_hash_string, conn)
_ ->
verify_and_publish(address_hash_string, files_array, conn)
PublishHelper.verify_and_publish(address_hash_string, files_array, conn)
end
end
else
EventsPublisher.broadcast(
prepare_verification_error(
PublishHelper.prepare_verification_error(
"Please attach JSON file with metadata of contract's compilation.",
address_hash_string,
conn
@ -160,150 +159,4 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
send_resp(conn, 204, "")
end
defp verify_and_publish(address_hash_string, files_array, conn) do
with {:ok, _verified_status} <- Sourcify.verify(address_hash_string, files_array),
{:ok, _verified_status} <- Sourcify.check_by_address(address_hash_string) do
get_metadata_and_publish(address_hash_string, conn)
else
{:error, "partial"} ->
{:ok, status, metadata} = Sourcify.check_by_address_any(address_hash_string)
process_metadata_and_publish(address_hash_string, metadata, status == "partial", conn)
{:error, %{"error" => error}} ->
EventsPublisher.broadcast(
prepare_verification_error(error, address_hash_string, conn),
:on_demand
)
{:error, error} ->
EventsPublisher.broadcast(
prepare_verification_error(error, address_hash_string, conn),
:on_demand
)
_ ->
EventsPublisher.broadcast(
prepare_verification_error("Unexpected error", address_hash_string, conn),
:on_demand
)
end
end
def get_metadata_and_publish(address_hash_string, conn) do
case Sourcify.get_metadata(address_hash_string) do
{:ok, verification_metadata} ->
process_metadata_and_publish(address_hash_string, verification_metadata, false, conn)
{:error, %{"error" => error}} ->
return_sourcify_error(conn, error, address_hash_string)
end
end
defp process_metadata_and_publish(address_hash_string, verification_metadata, is_partial, conn \\ nil) do
case Sourcify.parse_params_from_sourcify(address_hash_string, verification_metadata) do
%{
"params_to_publish" => params_to_publish,
"abi" => abi,
"secondary_sources" => secondary_sources,
"compilation_target_file_path" => compilation_target_file_path
} ->
ContractController.publish(conn, %{
"addressHash" => address_hash_string,
"params" => Map.put(params_to_publish, "partially_verified", is_partial),
"abi" => abi,
"secondarySources" => secondary_sources,
"compilationTargetFilePath" => compilation_target_file_path
})
{:error, :metadata} ->
return_sourcify_error(conn, Sourcify.no_metadata_message(), address_hash_string)
_ ->
return_sourcify_error(conn, Sourcify.failed_verification_message(), address_hash_string)
end
end
defp return_sourcify_error(nil, error, _address_hash_string) do
{:error, error: error}
end
defp return_sourcify_error(conn, error, address_hash_string) do
EventsPublisher.broadcast(
prepare_verification_error(error, address_hash_string, conn),
:on_demand
)
end
def prepare_files_array(files) do
if is_map(files), do: Enum.map(files, fn {_, file} -> file end), else: []
end
defp get_one_json(files_array) do
files_array
|> Enum.filter(fn file -> file.content_type == "application/json" end)
|> Enum.at(0)
end
# sobelow_skip ["Traversal.FileModule"]
defp read_files(plug_uploads) do
Enum.reduce(plug_uploads, %{}, fn %Plug.Upload{path: path, filename: file_name}, acc ->
{:ok, file_content} = File.read(path)
Map.put(acc, file_name, file_content)
end)
end
defp prepare_verification_error(msg, address_hash_string, conn) do
[
{:contract_verification_result,
{address_hash_string,
{:error,
%Changeset{
action: :insert,
errors: [
file: {msg, []}
],
data: %SmartContract{address_hash: address_hash_string},
valid?: false
}}, conn}}
]
end
def parse_optimization_runs(%{"runs" => runs}) do
case Integer.parse(runs) do
{integer, ""} -> integer
_ -> 200
end
end
def check_and_verify(address_hash_string) do
if Chain.smart_contract_fully_verified?(address_hash_string) do
{:ok, :already_fully_verified}
else
if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
if Chain.smart_contract_verified?(address_hash_string) do
case Sourcify.check_by_address(address_hash_string) do
{:ok, _verified_status} ->
get_metadata_and_publish(address_hash_string, nil)
_ ->
{:error, :not_verified}
end
else
case Sourcify.check_by_address_any(address_hash_string) do
{:ok, "full", metadata} ->
process_metadata_and_publish(address_hash_string, metadata, false)
{:ok, "partial", metadata} ->
process_metadata_and_publish(address_hash_string, metadata, true)
_ ->
{:error, :not_verified}
end
end
else
{:error, :sourcify_disabled}
end
end
end
end

@ -50,11 +50,4 @@ defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController do
send_resp(conn, 204, "")
end
def parse_optimization_runs(%{"runs" => runs}) do
case Integer.parse(runs) do
{integer, ""} -> integer
_ -> 200
end
end
end

@ -1,10 +1,10 @@
defmodule BlockScoutWeb.AddressContractVerificationViaJsonController do
use BlockScoutWeb, :controller
alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController
alias BlockScoutWeb.Controller
alias Explorer.Chain
alias Explorer.Chain.SmartContract
alias Explorer.SmartContract.Solidity.PublishHelper
alias Explorer.ThirdPartyIntegrations.Sourcify
def new(conn, %{"address_id" => address_hash_string}) do
@ -18,7 +18,7 @@ defmodule BlockScoutWeb.AddressContractVerificationViaJsonController do
else
case Sourcify.check_by_address(address_hash_string) do
{:ok, _verified_status} ->
VerificationController.get_metadata_and_publish(address_hash_string, conn)
PublishHelper.get_metadata_and_publish(address_hash_string, conn)
redirect(conn, to: address_contract_path)
_ ->

@ -48,11 +48,4 @@ defmodule BlockScoutWeb.AddressContractVerificationVyperController do
send_resp(conn, 204, "")
end
def parse_optimization_runs(%{"runs" => runs}) do
case Integer.parse(runs) do
{integer, ""} -> integer
_ -> 200
end
end
end

@ -3,15 +3,13 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
require Logger
alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController
alias BlockScoutWeb.API.RPC.{AddressController, Helpers}
alias Explorer.Chain
alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.{Address, Hash, SmartContract}
alias Explorer.Chain.SmartContract.VerificationStatus
alias Explorer.Etherscan.Contracts
alias Explorer.SmartContract.Helper
alias Explorer.SmartContract.Solidity.Publisher
alias Explorer.SmartContract.Solidity.{Publisher, PublishHelper}
alias Explorer.SmartContract.Solidity.PublisherWorker, as: SolidityPublisherWorker
alias Explorer.SmartContract.Vyper.Publisher, as: VyperPublisher
alias Explorer.ThirdPartyIntegrations.Sourcify
@ -160,7 +158,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end
defp prepare_params(files) when is_map(files) do
{:ok, VerificationController.prepare_files_array(files)}
{:ok, PublishHelper.prepare_files_array(files)}
end
defp prepare_params(files) when is_list(files) do
@ -240,7 +238,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
secondary_sources,
conn
) do
case publish_without_broadcast(%{
case PublishHelper.publish_without_broadcast(%{
"addressHash" => address_hash_string,
"params" => params_to_publish,
"abi" => abi,
@ -312,53 +310,6 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end
end
def publish_without_broadcast(
%{"addressHash" => address_hash, "abi" => abi, "compilationTargetFilePath" => file_path} = input
) do
params = proccess_params(input)
address_hash
|> Publisher.publish_smart_contract(params, abi, file_path)
|> proccess_response()
end
def publish_without_broadcast(%{"addressHash" => address_hash, "abi" => abi} = input) do
params = proccess_params(input)
address_hash
|> Publisher.publish_smart_contract(params, abi)
|> proccess_response()
end
def publish(nil, %{"addressHash" => _address_hash} = input) do
publish_without_broadcast(input)
end
def publish(conn, %{"addressHash" => address_hash} = input) do
result = publish_without_broadcast(input)
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand)
end
def proccess_params(input) do
if Map.has_key?(input, "secondarySources") do
input["params"]
|> Map.put("secondary_sources", Map.get(input, "secondarySources"))
else
input["params"]
end
end
def proccess_response(response) do
case response do
{:ok, _contract} = result ->
result
{:error, changeset} ->
{:error, changeset}
end
end
def listcontracts(conn, params) do
with pagination_options <- Helpers.put_pagination_options(%{}, params),
{:params, {:ok, options}} <- {:params, add_filters(pagination_options, params)} do
@ -400,7 +351,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
def getsourcecode(conn, params) do
with {:address_param, {:ok, address_param}} <- fetch_address(params),
{:format, {:ok, address_hash}} <- to_address_hash(address_param) do
_ = VerificationController.check_and_verify(address_param)
_ = PublishHelper.check_and_verify(address_param)
address = Contracts.address_hash_to_address_with_source_code(address_hash)
render(conn, :getsourcecode, %{
@ -503,7 +454,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end
defp to_smart_contract(address_hash) do
_ = VerificationController.check_and_verify(Hash.to_string(address_hash))
_ = PublishHelper.check_and_verify(Hash.to_string(address_hash))
result =
case Chain.address_hash_to_smart_contract(address_hash) do

@ -47,4 +47,28 @@ defmodule BlockScoutWeb.API.V2.FallbackController do
|> put_view(ApiView)
|> render(:message, %{message: "Restricted access"})
end
def call(conn, {:already_verified, true}) do
conn
|> put_view(ApiView)
|> render(:message, %{message: "Already verified"})
end
def call(conn, {:no_json_file, _}) do
conn
|> put_view(ApiView)
|> render(:message, %{message: "JSON files not found"})
end
def call(conn, {:file_error, _}) do
conn
|> put_view(ApiView)
|> render(:message, %{message: "Error while reading JSON file"})
end
def call(conn, {:libs_format, _}) do
conn
|> put_view(ApiView)
|> render(:message, %{message: "Libraries are not valid JSON map"})
end
end

@ -4,11 +4,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do
import Explorer.SmartContract.Solidity.Verifier, only: [parse_boolean: 1]
alias BlockScoutWeb.{AccessHelpers, AddressView}
alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController
alias Ecto.Association.NotLoaded
alias Explorer.Chain
alias Explorer.Chain.SmartContract
alias Explorer.SmartContract.{Reader, Writer}
alias Explorer.SmartContract.Solidity.PublishHelper
@smart_contract_address_options [
necessity_by_association: %{
@ -25,7 +25,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do
def smart_contract(conn, %{"address_hash" => address_hash_string} = params) do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
_ <- VerificationController.check_and_verify(address_hash_string),
_ <- PublishHelper.check_and_verify(address_hash_string),
{:not_found, {:ok, address}} <-
{:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options, true)} do
conn

@ -0,0 +1,178 @@
defmodule BlockScoutWeb.API.V2.VerificationController do
use BlockScoutWeb, :controller
import Explorer.SmartContract.Solidity.Verifier, only: [parse_boolean: 1]
alias BlockScoutWeb.AccessHelpers
alias BlockScoutWeb.API.V2.ApiView
alias Explorer.Chain
alias Explorer.SmartContract.Solidity.PublisherWorker, as: SolidityPublisherWorker
alias Explorer.SmartContract.Solidity.PublishHelper
alias Explorer.SmartContract.Vyper.PublisherWorker, as: VyperPublisherWorker
alias Explorer.SmartContract.{CompilerVersion, RustVerifierInterface, Solidity.CodeCompiler}
action_fallback(BlockScoutWeb.API.V2.FallbackController)
def config(conn, _params) do
evm_versions = CodeCompiler.allowed_evm_versions()
solidity_compiler_versions = CompilerVersion.fetch_version_list(:solc)
vyper_compiler_versions = CompilerVersion.fetch_version_list(:vyper)
verification_options =
["flattened_code", "standard_input", "vyper_code"]
|> (&if(Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled],
do: ["sourcify" | &1],
else: &1
)).()
|> (&if(RustVerifierInterface.enabled?(), do: ["multi_part" | &1], else: &1)).()
conn
|> json(%{
evm_versions: evm_versions,
solidity_compiler_versions: solidity_compiler_versions,
vyper_compiler_versions: vyper_compiler_versions,
verification_options: verification_options
})
end
def verification_via_flattened_code(
conn,
%{"address_hash" => address_hash_string, "compiler_version" => compiler_version, "source_code" => source_code} =
params
) do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
{:already_verified, false} <- {:already_verified, Chain.smart_contract_fully_verified?(address_hash)} do
verification_params =
%{
"address_hash" => String.downcase(address_hash_string),
"compiler_version" => compiler_version,
"contract_source_code" => source_code
}
|> Map.put("optimization", Map.get(params, "is_optimization_enabled", false))
|> (&if(params |> Map.get("is_optimization_enabled", false) |> parse_boolean(),
do: Map.put(&1, "optimization_runs", Map.get(params, "optimization_runs", 200)),
else: &1
)).()
|> Map.put("evm_version", Map.get(params, "evm_version", "default"))
|> Map.put("autodetect_constructor_args", Map.get(params, "autodetect_constructor_args", true))
|> Map.put("constructor_arguments", Map.get(params, "constructor_args", ""))
|> Map.put("name", Map.get(params, "contract_name", ""))
|> Map.put("external_libraries", Map.get(params, "libraries", %{}))
Que.add(SolidityPublisherWorker, {"flattened_api_v2", verification_params})
conn
|> put_view(ApiView)
|> render(:message, %{message: "Verification started"})
end
end
# sobelow_skip ["Traversal.FileModule"]
def verification_via_standard_input(
conn,
%{"address_hash" => address_hash_string, "files" => files, "compiler_version" => compiler_version} = params
) do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
{:already_verified, false} <- {:already_verified, Chain.smart_contract_fully_verified?(address_hash)},
files_array <- PublishHelper.prepare_files_array(files),
{:no_json_file, %Plug.Upload{path: path}} <-
{:no_json_file, PublishHelper.get_one_json(files_array)},
{:file_error, {:ok, json_input}} <- {:file_error, File.read(path)} do
verification_params =
%{
"address_hash" => String.downcase(address_hash_string),
"compiler_version" => compiler_version
}
|> Map.put("autodetect_constructor_args", Map.get(params, "autodetect_constructor_args", false))
|> Map.put("constructor_arguments", Map.get(params, "constructor_args", ""))
|> Map.put("name", Map.get(params, "contract_name", ""))
Que.add(SolidityPublisherWorker, {"json_api_v2", verification_params, json_input})
conn
|> put_view(ApiView)
|> render(:message, %{message: "Verification started"})
end
end
def verification_via_sourcify(conn, %{"address_hash" => address_hash_string, "files" => files} = params) do
with {:not_found, true} <-
{:not_found, Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled]},
{:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
{:already_verified, false} <- {:already_verified, Chain.smart_contract_fully_verified?(address_hash)},
files_array <- PublishHelper.prepare_files_array(files),
{:no_json_file, %Plug.Upload{path: _path}} <-
{:no_json_file, PublishHelper.get_one_json(files_array)},
files_content <- PublishHelper.read_files(files_array) do
Que.add(SolidityPublisherWorker, {"sourcify_api_v2", address_hash_string, files_content, conn})
conn
|> put_view(ApiView)
|> render(:message, %{message: "Verification started"})
end
end
def verification_via_multi_part(
conn,
%{"address_hash" => address_hash_string, "compiler_version" => compiler_version, "files" => files} = params
) do
with {:not_found, true} <- {:not_found, RustVerifierInterface.enabled?()},
{:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
{:already_verified, false} <- {:already_verified, Chain.smart_contract_fully_verified?(address_hash)},
libraries <- Map.get(params, "libraries", "{}"),
{:libs_format, {:ok, json}} <- {:libs_format, Jason.decode(libraries)} do
verification_params =
%{
"address_hash" => String.downcase(address_hash_string),
"compiler_version" => compiler_version
}
|> Map.put("optimization", Map.get(params, "is_optimization_enabled", false))
|> (&if(params |> Map.get("is_optimization_enabled", false) |> parse_boolean(),
do: Map.put(&1, "optimization_runs", Map.get(params, "optimization_runs", 200)),
else: &1
)).()
|> Map.put("evm_version", Map.get(params, "evm_version", "default"))
|> Map.put("external_libraries", json)
files_array =
files
|> Map.values()
|> PublishHelper.read_files()
Que.add(SolidityPublisherWorker, {"multipart_api_v2", verification_params, files_array})
conn
|> put_view(ApiView)
|> render(:message, %{message: "Verification started"})
end
end
def verification_via_vyper_code(
conn,
%{"address_hash" => address_hash_string, "compiler_version" => compiler_version, "source_code" => source_code} =
params
) do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params),
{:already_verified, false} <- {:already_verified, Chain.smart_contract_fully_verified?(address_hash)} do
verification_params =
%{
"address_hash" => String.downcase(address_hash_string),
"compiler_version" => compiler_version,
"contract_source_code" => source_code
}
|> Map.put("constructor_arguments", Map.get(params, "constructor_args", "") || "")
|> Map.put("name", Map.get(params, "contract_name", "Vyper_contract"))
Que.add(VyperPublisherWorker, {address_hash_string, verification_params})
conn
|> put_view(ApiView)
|> render(:message, %{message: "Verification started"})
end
end
end

@ -48,6 +48,18 @@ defmodule BlockScoutWeb.Notifier do
Enum.each(address_current_token_balances, &broadcast_address_token_balance/1)
end
def handle_event(
{:chain_event, :contract_verification_result, :on_demand, {address_hash, contract_verification_result}}
) do
Endpoint.broadcast(
"addresses:#{address_hash}",
"verification_result",
%{
result: contract_verification_result
}
)
end
def handle_event(
{:chain_event, :contract_verification_result, :on_demand, {address_hash, contract_verification_result, conn}}
) do

@ -27,5 +27,15 @@ defmodule BlockScoutWeb.SmartContractsApiV2Router do
get("/:address_hash/methods-read-proxy", V2.SmartContractController, :methods_read_proxy)
get("/:address_hash/methods-write-proxy", V2.SmartContractController, :methods_write_proxy)
post("/:address_hash/query-read-method", V2.SmartContractController, :query_read_method)
get("/verification/config", V2.VerificationController, :config)
scope "/:address_hash/verification/via" do
post("/flattened-code", V2.VerificationController, :verification_via_flattened_code)
post("/standard-input", V2.VerificationController, :verification_via_standard_input)
post("/sourcify", V2.VerificationController, :verification_via_sourcify)
post("/multi-part", V2.VerificationController, :verification_via_multi_part)
post("/vyper-code", V2.VerificationController, :verification_via_vyper_code)
end
end
end

@ -16,7 +16,7 @@
<div class="dropzone-1 dropzone-previews" style="display: flex; margin: 0 auto;", id="dropzone-previews">
<div style="text-align: center;">
<span class="dz-message btn-full-primary" id="dz-button-message"><%= gettext("Drop sources and metadata JSON file or click here") %></span>
<%= error_tag f, :file, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %>
<%= error_tag f, :files, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %>
</div>
</div>
</div>

@ -65,7 +65,7 @@
<div class="dropzone-1 dropzone-previews" style="display: flex; margin: 0 auto;", id="dropzone-previews">
<div style="text-align: center;">
<span class="dz-message btn-full-primary" id="dz-button-message"><%= gettext("Drop sources or click here") %></span>
<%= error_tag f, :file, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %>
<%= error_tag f, :files, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %>
</div>
</div>
</div>

@ -26,7 +26,7 @@
<div class="dropzone-1 dropzone-previews" style="display: flex; margin: 0 auto;", id="dropzone-previews">
<div style="text-align: center;">
<span class="dz-message btn-full-primary" id="dz-button-message"><%= gettext("Drop the standard input JSON file or click here") %></span>
<%= error_tag f, :file, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %>
<%= error_tag f, :files, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %>
</div>
</div>
</div>

@ -5,6 +5,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
alias BlockScoutWeb.API.V2.TransactionView
alias BlockScoutWeb.SmartContractView
alias BlockScoutWeb.{ABIEncodedValueView, AddressContractView, AddressView}
alias Ecto.Changeset
alias Explorer.Chain
alias Explorer.Chain.{Address, SmartContract}
alias Explorer.Visualize.Sol2uml
@ -23,6 +24,14 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
prepare_function_response(output, names, contract_address_hash)
end
def render("changeset_errors.json", %{changeset: changeset}) do
Changeset.traverse_errors(changeset, fn {msg, opts} ->
Regex.replace(~r"%{(\w+)}", msg, fn _, key ->
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
end)
end)
end
def prepare_function_response(outputs, names, contract_address_hash) do
case outputs do
{:error, %{code: code, message: message, data: data}} ->

@ -353,7 +353,7 @@ defmodule Explorer.Chain.SmartContract do
else: validate_required(&1, [:name, :compiler_version, :optimization, :address_hash, :contract_code_md5])
)).()
field_to_put_message = if json_verification, do: :file, else: select_error_field(error)
field_to_put_message = if json_verification, do: :files, else: select_error_field(error)
if error_message do
add_error(validated, field_to_put_message, error_message(error, error_message))

@ -19,6 +19,16 @@ defmodule Explorer.SmartContract.CompilerVersion do
end
end
def fetch_version_list(compiler) do
case fetch_versions(compiler) do
{:ok, compiler_versions} ->
compiler_versions
{:error, _} ->
[]
end
end
defp fetch_solc_versions do
if RustVerifierInterface.enabled?() do
RustVerifierInterface.get_versions_list()
@ -156,7 +166,7 @@ defmodule Explorer.SmartContract.CompilerVersion do
case compiler do
:solc ->
if compiler_version == "latest" do
compiler_versions = get_compiler_versions(:solc)
compiler_versions = fetch_version_list(:solc)
if Enum.count(compiler_versions) > 1 do
latest_stable_version =
@ -180,7 +190,7 @@ defmodule Explorer.SmartContract.CompilerVersion do
:vyper ->
if compiler_version == "latest" do
compiler_versions = get_compiler_versions(:vyper)
compiler_versions = fetch_version_list(:vyper)
if Enum.count(compiler_versions) > 1 do
latest_stable_version =
@ -196,14 +206,4 @@ defmodule Explorer.SmartContract.CompilerVersion do
end
end
end
defp get_compiler_versions(compiler) do
case fetch_versions(compiler) do
{:ok, compiler_versions} ->
compiler_versions
{:error, _} ->
[]
end
end
end

@ -69,7 +69,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
}
}
"""
@spec run(Keyword.t()) :: {:ok, map} | {:error, :compilation | :name}
@spec run(Keyword.t()) :: {:ok, map} | {:error, :compilation | :name} | {:error, :compilation, String.t()}
def run(params) do
name = Keyword.fetch!(params, :name)
compiler_version = Keyword.fetch!(params, :compiler_version)

@ -0,0 +1,217 @@
defmodule Explorer.SmartContract.Solidity.PublishHelper do
@moduledoc """
Module responsible for preparing and publishing smart contracts
"""
alias Ecto.Changeset
alias Explorer.Chain
alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.SmartContract
alias Explorer.SmartContract.Solidity.Publisher
alias Explorer.ThirdPartyIntegrations.Sourcify
def verify_and_publish(address_hash_string, files_array, conn, api_v2? \\ false) do
with {:ok, _verified_status} <- Sourcify.verify(address_hash_string, files_array),
{:ok, _verified_status} <- Sourcify.check_by_address(address_hash_string) do
get_metadata_and_publish(address_hash_string, conn, api_v2?)
else
{:error, "partial"} ->
{:ok, status, metadata} = Sourcify.check_by_address_any(address_hash_string)
process_metadata_and_publish(address_hash_string, metadata, status == "partial", conn, api_v2?)
{:error, %{"error" => error}} ->
EventsPublisher.broadcast(
prepare_verification_error(error, address_hash_string, conn, api_v2?),
:on_demand
)
{:error, error} ->
EventsPublisher.broadcast(
prepare_verification_error(error, address_hash_string, conn, api_v2?),
:on_demand
)
_ ->
EventsPublisher.broadcast(
prepare_verification_error("Unexpected error", address_hash_string, conn, api_v2?),
:on_demand
)
end
end
def get_metadata_and_publish(address_hash_string, conn, api_v2? \\ false) do
case Sourcify.get_metadata(address_hash_string) do
{:ok, verification_metadata} ->
process_metadata_and_publish(address_hash_string, verification_metadata, false, conn, api_v2?)
{:error, %{"error" => error}} ->
return_sourcify_error(conn, error, address_hash_string, api_v2?)
end
end
defp process_metadata_and_publish(address_hash_string, verification_metadata, is_partial, conn \\ nil, api_v2?) do
case Sourcify.parse_params_from_sourcify(address_hash_string, verification_metadata) do
%{
"params_to_publish" => params_to_publish,
"abi" => abi,
"secondary_sources" => secondary_sources,
"compilation_target_file_path" => compilation_target_file_path
} ->
publish(
conn,
%{
"addressHash" => address_hash_string,
"params" => Map.put(params_to_publish, "partially_verified", is_partial),
"abi" => abi,
"secondarySources" => secondary_sources,
"compilationTargetFilePath" => compilation_target_file_path
},
api_v2?
)
{:error, :metadata} ->
return_sourcify_error(conn, Sourcify.no_metadata_message(), address_hash_string, api_v2?)
_ ->
return_sourcify_error(conn, Sourcify.failed_verification_message(), address_hash_string, api_v2?)
end
end
defp return_sourcify_error(nil, error, _address_hash_string, _api_v2?) do
{:error, error: error}
end
defp return_sourcify_error(conn, error, address_hash_string, api_v2?) do
EventsPublisher.broadcast(
prepare_verification_error(error, address_hash_string, conn, api_v2?),
:on_demand
)
end
def prepare_files_array(files) do
if is_map(files), do: Enum.map(files, fn {_, file} -> file end), else: []
end
def get_one_json(files_array) do
files_array
|> Enum.filter(fn file -> file.content_type == "application/json" end)
|> Enum.at(0)
end
# sobelow_skip ["Traversal.FileModule"]
def read_files(plug_uploads) do
Enum.reduce(plug_uploads, %{}, fn %Plug.Upload{path: path, filename: file_name}, acc ->
{:ok, file_content} = File.read(path)
Map.put(acc, file_name, file_content)
end)
end
def prepare_verification_error(msg, address_hash_string, conn, api_v2? \\ false)
def prepare_verification_error(msg, address_hash_string, conn, false) do
[
{:contract_verification_result,
{address_hash_string,
{:error,
%Changeset{
action: :insert,
errors: [
files: {msg, []}
],
data: %SmartContract{address_hash: address_hash_string},
valid?: false
}}, conn}}
]
end
def prepare_verification_error(msg, address_hash_string, _conn, true) do
changeset =
SmartContract.invalid_contract_changeset(%SmartContract{address_hash: address_hash_string}, %{}, msg, nil, true)
[
{:contract_verification_result, {address_hash_string, {:error, changeset}}}
]
end
def check_and_verify(address_hash_string) do
if Chain.smart_contract_fully_verified?(address_hash_string) do
{:ok, :already_fully_verified}
else
if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
if Chain.smart_contract_verified?(address_hash_string) do
case Sourcify.check_by_address(address_hash_string) do
{:ok, _verified_status} ->
get_metadata_and_publish(address_hash_string, nil)
_ ->
{:error, :not_verified}
end
else
case Sourcify.check_by_address_any(address_hash_string) do
{:ok, "full", metadata} ->
process_metadata_and_publish(address_hash_string, metadata, false, false)
{:ok, "partial", metadata} ->
process_metadata_and_publish(address_hash_string, metadata, true, false)
_ ->
{:error, :not_verified}
end
end
else
{:error, :sourcify_disabled}
end
end
end
def publish_without_broadcast(
%{"addressHash" => address_hash, "abi" => abi, "compilationTargetFilePath" => file_path} = input
) do
params = proccess_params(input)
address_hash
|> Publisher.publish_smart_contract(params, abi, file_path)
|> proccess_response()
end
def publish_without_broadcast(%{"addressHash" => address_hash, "abi" => abi} = input) do
params = proccess_params(input)
address_hash
|> Publisher.publish_smart_contract(params, abi)
|> proccess_response()
end
def publish(nil, %{"addressHash" => _address_hash} = input, _) do
publish_without_broadcast(input)
end
def publish(conn, %{"addressHash" => address_hash} = input, api_v2?) do
result = publish_without_broadcast(input)
if api_v2? do
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result}}], :on_demand)
else
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand)
end
end
def proccess_params(input) do
if Map.has_key?(input, "secondarySources") do
input["params"]
|> Map.put("secondary_sources", Map.get(input, "secondarySources"))
else
input["params"]
end
end
def proccess_response(response) do
case response do
{:ok, _contract} = result ->
result
{:error, changeset} ->
{:error, changeset}
end
end
end

@ -114,7 +114,7 @@ defmodule Explorer.SmartContract.Solidity.Publisher do
end
end
def publish_with_multi_part_files(%{"address_hash" => address_hash} = params, external_libraries, files) do
def publish_with_multi_part_files(%{"address_hash" => address_hash} = params, external_libraries \\ %{}, files) do
params_with_external_libaries = add_external_libraries(params, external_libraries)
case Verifier.evaluate_authenticity_via_multi_part_files(address_hash, params_with_external_libaries, files) do
@ -283,6 +283,8 @@ defmodule Explorer.SmartContract.Solidity.Publisher do
end)
end
defp add_external_libraries(%{"external_libraries" => _} = params, _external_libraries), do: params
defp add_external_libraries(params, external_libraries) do
clean_external_libraries =
Enum.reduce(1..Application.get_env(:block_scout_web, :verification_max_libraries), %{}, fn number, acc ->

@ -7,7 +7,8 @@ defmodule Explorer.SmartContract.Solidity.PublisherWorker do
alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.SmartContract.VerificationStatus
alias Explorer.SmartContract.Solidity.Publisher
alias Explorer.SmartContract.Solidity.{Publisher, PublishHelper}
alias Explorer.ThirdPartyIntegrations.Sourcify
def perform({"flattened", %{"address_hash" => address_hash} = params, external_libraries, conn}) do
result =
@ -60,4 +61,54 @@ defmodule Explorer.SmartContract.Solidity.PublisherWorker do
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand)
end
def perform({"flattened_api_v2", %{"address_hash" => address_hash} = params}) do
result =
case Publisher.publish(address_hash, params) do
{:ok, _contract} = result ->
result
{:error, changeset} ->
{:error, changeset}
end
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result}}], :on_demand)
end
def perform({"sourcify_api_v2", address_hash_string, files_array, conn}) do
case Sourcify.check_by_address(address_hash_string) do
{:ok, _verified_status} ->
PublishHelper.get_metadata_and_publish(address_hash_string, conn, true)
_ ->
PublishHelper.verify_and_publish(address_hash_string, files_array, conn, true)
end
end
def perform({"json_api_v2", %{"address_hash" => address_hash} = params, json_input}) do
result =
case Publisher.publish_with_standard_json_input(params, json_input) do
{:ok, _contract} = result ->
result
{:error, changeset} ->
{:error, changeset}
end
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result}}], :on_demand)
end
def perform({"multipart_api_v2", %{"address_hash" => address_hash} = params, files_map})
when is_map(files_map) do
result =
case Publisher.publish_with_multi_part_files(params, files_map) do
{:ok, _contract} = result ->
result
{:error, changeset} ->
{:error, changeset}
end
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result}}], :on_demand)
end
end

@ -23,17 +23,18 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
do: {:error, :contract_source_code}
def evaluate_authenticity(address_hash, params) do
try do
evaluate_authenticity_inner(RustVerifierInterface.enabled?(), address_hash, params)
rescue
exception ->
Logger.error(fn ->
[
"Error while verifying smart-contract address: #{address_hash}, params: #{inspect(params, limit: :infinity, printable_limit: :infinity)}: ",
Exception.format(:error, exception)
]
end)
end
# TODO: remove comments
# try do
evaluate_authenticity_inner(RustVerifierInterface.enabled?(), address_hash, params)
# rescue
# exception ->
# Logger.error(fn ->
# [
# "Error while verifying smart-contract address: #{address_hash}, params: #{inspect(params, limit: :infinity, printable_limit: :infinity)}: ",
# Exception.format(:error, exception)
# ]
# end)
# end
end
defp evaluate_authenticity_inner(true, address_hash, params) do
@ -90,6 +91,10 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
defp prepare_optimization_runs(false_, _) when false_ in [false, "false"], do: nil
defp prepare_optimization_runs(true_, runs) when true_ in [true, "true"] and is_number(runs) do
runs
end
defp prepare_optimization_runs(true_, runs) when true_ in [true, "true"] do
case Integer.parse(runs) do
{runs_integer, ""} ->
@ -240,7 +245,7 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
constructor_arguments = Map.get(params, "constructor_arguments", "")
evm_version = Map.get(params, "evm_version")
optimization_runs = Map.get(params, "optimization_runs", 200)
autodetect_constructor_arguments = params |> Map.get("autodetect_constructor_args", "false") |> parse_boolean()
autodetect_constructor_arguments = params |> Map.get("autodetect_constructor_args", "true") |> parse_boolean()
if is_compiler_version_at_least_0_6_0?(compiler_version) do
Enum.reduce_while(@bytecode_hash_options, false, fn option, acc ->
@ -298,18 +303,22 @@ defmodule Explorer.SmartContract.Solidity.Verifier do
defp is_compiler_version_at_least_0_6_0?("latest"), do: true
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)
case compiler_version |> String.split("+", parts: 2) do
[version, _] ->
digits =
version
|> String.replace("v", "")
|> String.split(".")
|> Enum.map(fn str ->
{num, _} = Integer.parse(str)
num
end)
Enum.fetch!(digits, 0) > 0 || Enum.fetch!(digits, 1) >= 6
Enum.fetch!(digits, 0) > 0 || Enum.fetch!(digits, 1) >= 6
_ ->
false
end
end
defp compare_bytecodes({:error, :name}, _, _, _), do: {:error, :name}

@ -40,6 +40,9 @@ defmodule Explorer.SmartContract.Vyper.Publisher do
{:error, error} ->
{:error, unverified_smart_contract(address_hash, params, error, nil)}
_ ->
{:error, unverified_smart_contract(address_hash, params, "Unexpected error", nil)}
end
end

@ -20,4 +20,17 @@ defmodule Explorer.SmartContract.Vyper.PublisherWorker do
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand)
end
def perform({address_hash, params}) do
result =
case Publisher.publish(address_hash, params) do
{:ok, _contract} = result ->
result
{:error, changeset} ->
{:error, changeset}
end
EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result}}], :on_demand)
end
end

@ -44,24 +44,38 @@ defmodule Explorer.ThirdPartyIntegrations.Sourcify do
|> Multipart.add_field("chain", chain_id)
|> Multipart.add_field("address", address_hash_string)
multipart_body =
files
|> Enum.reduce(multipart_text_params, fn file, acc ->
if file do
acc
|> Multipart.add_file(file.path,
name: "files",
file_name: Path.basename(file.path)
)
else
acc
end
end)
multipart_body = prepare_body_for_sourcify(files, multipart_text_params)
http_post_request(verify_url(), multipart_body)
end
# sobelow_skip ["Traversal.FileModule"]
defp prepare_body_for_sourcify(files, multipart_text_params) when is_map(files) do
files
|> Enum.reduce(multipart_text_params, fn {name, content}, acc ->
if content do
acc
|> Multipart.add_file_content(content, name, name: "files")
else
acc
end
end)
end
defp prepare_body_for_sourcify(files, multipart_text_params) do
files
|> Enum.reduce(multipart_text_params, fn file, acc ->
if file do
acc
|> Multipart.add_file(file.path,
name: "files",
file_name: Path.basename(file.path)
)
else
acc
end
end)
end
def verify_via_rust_microservice(address_hash_string, files) do
chain_id = config(__MODULE__, :chain_id)
@ -70,27 +84,7 @@ defmodule Explorer.ThirdPartyIntegrations.Sourcify do
|> Map.put("chain", chain_id)
|> Map.put("address", address_hash_string)
files_body =
files
|> Enum.reduce(Map.new(), fn file, acc ->
if file do
{:ok, file_content} = File.read(file.path)
file_content =
if Helper.json_file?(file.filename) do
file_content
|> Jason.decode!()
|> Jason.encode!()
else
file_content
end
acc
|> Map.put(file.filename, file_content)
else
acc
end
end)
files_body = prepare_body_for_microservice(files)
body =
body_params
@ -99,6 +93,51 @@ defmodule Explorer.ThirdPartyIntegrations.Sourcify do
http_post_request_rust_microservice(verify_url_rust_microservice(), body)
end
defp prepare_body_for_microservice(files) when is_map(files) do
files
|> Enum.reduce(Map.new(), fn {name, content}, acc ->
if content do
file_content =
if Helper.json_file?(name) do
content
|> Jason.decode!()
|> Jason.encode!()
else
content
end
acc
|> Map.put(name, file_content)
else
acc
end
end)
end
# sobelow_skip ["Traversal.FileModule"]
defp prepare_body_for_microservice(files) do
files
|> Enum.reduce(Map.new(), fn file, acc ->
if file do
{:ok, file_content} = File.read(file.path)
file_content =
if Helper.json_file?(file.filename) do
file_content
|> Jason.decode!()
|> Jason.encode!()
else
file_content
end
acc
|> Map.put(file.filename, file_content)
else
acc
end
end)
end
def http_get_request(url, params) do
request = HTTPoison.get(url, [], params: params)
@ -118,10 +157,7 @@ defmodule Explorer.ThirdPartyIntegrations.Sourcify do
{:error, %Error{reason: reason}} ->
{:error, reason}
{:error, :nxdomain} ->
{:error, "Sourcify is not responsive"}
{:error, _} ->
_ ->
{:error, "Unexpected response from Sourcify"}
end
end

@ -309,7 +309,8 @@ defmodule Explorer.SmartContract.Solidity.VerifierTest do
"evm_version" => "petersburg",
"name" => "TestToken",
"optimization" => false,
"constructor_arguments" => wrong_constructor_arguments
"constructor_arguments" => wrong_constructor_arguments,
"autodetect_constructor_args" => false
}
assert {:error, :constructor_arguments} = Verifier.evaluate_authenticity(contract_address_1.hash, params_1)

Loading…
Cancel
Save