diff --git a/CHANGELOG.md b/CHANGELOG.md index 946fcd1e45..2ca108a165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Current ### Features +- [#2857](https://github.com/poanetwork/blockscout/pull/2857) - Extend getsourcecode API view with new output fields - [#2787](https://github.com/poanetwork/blockscout/pull/2787) - async fetching of address counters - [#2791](https://github.com/poanetwork/blockscout/pull/2791) - add ipc client - [#2449](https://github.com/poanetwork/blockscout/pull/2449) - add ability to send notification events through postgres notify diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex index 8f996a2bcc..0d0557469b 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex @@ -35,7 +35,11 @@ defmodule BlockScoutWeb.API.RPC.ContractView do "CompilerVersion" => "", "DecompiledSourceCode" => "", "DecompilerVersion" => decompiler_version(nil), - "OptimizationUsed" => "" + "OptimizationUsed" => "", + "OptimizationRuns" => "", + "EVMVersion" => "", + "ConstructorArguments" => "", + "ExternalLibraries" => "" } end @@ -43,6 +47,64 @@ defmodule BlockScoutWeb.API.RPC.ContractView do decompiled_smart_contract = latest_decompiled_smart_contract(address.decompiled_smart_contracts) contract = address.smart_contract || %{} + optimization = Map.get(contract, :optimization, "") + + contract_output = %{ + "Address" => to_string(address.hash) + } + + contract_output + |> set_decompiled_contract_data(decompiled_smart_contract) + |> set_optimization_runs(contract, optimization) + |> set_constructor_arguments(contract) + |> set_external_libraries(contract) + |> set_verified_contract_data(contract, address, optimization) + end + + defp set_decompiled_contract_data(contract_output, decompiled_smart_contract) do + if decompiled_smart_contract do + contract_output + |> Map.put_new(:DecompiledSourceCode, decompiled_source_code(decompiled_smart_contract)) + |> Map.put_new(:DecompilerVersion, decompiler_version(decompiled_smart_contract)) + else + contract_output + end + end + + defp set_optimization_runs(contract_output, contract, optimization) do + optimization_runs = Map.get(contract, :optimization_runs, "") + + if optimization && optimization != "" do + contract_output + |> Map.put_new(:OptimizationRuns, optimization_runs) + else + contract_output + end + end + + defp set_constructor_arguments(contract_output, contract) do + constructor_arguments = Map.get(contract, :constructor_arguments, "") + + if constructor_arguments && constructor_arguments != "" do + contract_output + |> Map.put_new(:ConstructorArguments, constructor_arguments) + else + contract_output + end + end + + defp set_external_libraries(contract_output, contract) do + external_libraries = Map.get(contract, :external_libraries, []) + + if Enum.count(external_libraries) > 0 do + contract_output + |> Map.put_new(:ExternalLibraries, external_libraries) + else + contract_output + end + end + + defp set_verified_contract_data(contract_output, contract, address, optimization) do contract_abi = if is_nil(address.smart_contract) do "Contract source code not verified" @@ -51,27 +113,28 @@ defmodule BlockScoutWeb.API.RPC.ContractView do end contract_optimization = - case Map.get(contract, :optimization, "") do + case optimization do true -> - "1" + "true" false -> - "0" + "false" "" -> "" end - %{ - "Address" => to_string(address.hash), - "SourceCode" => Map.get(contract, :contract_source_code, ""), - "ABI" => contract_abi, - "ContractName" => Map.get(contract, :name, ""), - "DecompiledSourceCode" => decompiled_source_code(decompiled_smart_contract), - "DecompilerVersion" => decompiler_version(decompiled_smart_contract), - "CompilerVersion" => Map.get(contract, :compiler_version, ""), - "OptimizationUsed" => contract_optimization - } + if Map.equal?(contract, %{}) do + contract_output + else + contract_output + |> Map.put_new(:SourceCode, Map.get(contract, :contract_source_code, "")) + |> Map.put_new(:ABI, contract_abi) + |> Map.put_new(:ContractName, Map.get(contract, :name, "")) + |> Map.put_new(:CompilerVersion, Map.get(contract, :compiler_version, "")) + |> Map.put_new(:OptimizationUsed, contract_optimization) + |> Map.put_new(:EVMVersion, Map.get(contract, :evm_version, "")) + end end defp prepare_contract(%Address{ @@ -80,10 +143,7 @@ defmodule BlockScoutWeb.API.RPC.ContractView do }) do %{ "Address" => to_string(hash), - "ABI" => "Contract source code not verified", - "ContractName" => "", - "CompilerVersion" => "", - "OptimizationUsed" => "" + "ABI" => "Contract source code not verified" } end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs index 37ce57f508..f4af65e9ac 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs @@ -70,10 +70,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == [ %{ "ABI" => "Contract source code not verified", - "Address" => to_string(address.hash), - "CompilerVersion" => "", - "ContractName" => "", - "OptimizationUsed" => "" + "Address" => to_string(address.hash) } ] @@ -95,10 +92,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == [ %{ "ABI" => "Contract source code not verified", - "Address" => to_string(address.hash), - "CompilerVersion" => "", - "ContractName" => "", - "OptimizationUsed" => "" + "Address" => to_string(address.hash) } ] @@ -124,10 +118,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == [ %{ "ABI" => "Contract source code not verified", - "Address" => to_string(address.hash), - "CompilerVersion" => "", - "ContractName" => "", - "OptimizationUsed" => "" + "Address" => to_string(address.hash) } ] @@ -174,10 +165,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == [ %{ "ABI" => "Contract source code not verified", - "Address" => to_string(decompiled_smart_contract.address_hash), - "CompilerVersion" => "", - "ContractName" => "", - "OptimizationUsed" => "" + "Address" => to_string(decompiled_smart_contract.address_hash) } ] @@ -199,10 +187,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == [ %{ "ABI" => "Contract source code not verified", - "Address" => to_string(smart_contract.address_hash), - "CompilerVersion" => "", - "ContractName" => "", - "OptimizationUsed" => "" + "Address" => to_string(smart_contract.address_hash) } ] @@ -225,10 +210,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert %{ "ABI" => "Contract source code not verified", - "Address" => to_string(smart_contract.address_hash), - "CompilerVersion" => "", - "ContractName" => "", - "OptimizationUsed" => "" + "Address" => to_string(smart_contract.address_hash) } in response["result"] refute to_string(non_match.address_hash) in Enum.map(response["result"], &Map.get(&1, "Address")) @@ -251,10 +233,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == [ %{ "ABI" => "Contract source code not verified", - "Address" => to_string(contract_address.hash), - "CompilerVersion" => "", - "ContractName" => "", - "OptimizationUsed" => "" + "Address" => to_string(contract_address.hash) } ] @@ -281,10 +260,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["result"] == [ %{ "ABI" => "Contract source code not verified", - "Address" => to_string(contract_address.hash), - "CompilerVersion" => "", - "ContractName" => "", - "OptimizationUsed" => "" + "Address" => to_string(contract_address.hash) } ] @@ -423,7 +399,11 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "CompilerVersion" => "", "OptimizationUsed" => "", "DecompiledSourceCode" => "", - "DecompilerVersion" => "" + "DecompilerVersion" => "", + "ConstructorArguments" => "", + "EVMVersion" => "", + "ExternalLibraries" => "", + "OptimizationRuns" => "" } ] @@ -439,7 +419,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do end test "with a verified contract address", %{conn: conn} do - contract = insert(:smart_contract, optimization: true) + contract = insert(:smart_contract, optimization: true, optimization_runs: 200, evm_version: "default") params = %{ "module" => "contract", @@ -456,12 +436,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "ABI" => Jason.encode!(contract.abi), "ContractName" => contract.name, "CompilerVersion" => contract.compiler_version, - "DecompiledSourceCode" => "Contract source code not decompiled.", # The contract's optimization value is true, so the expected value # for `OptimizationUsed` is "1". If it was false, the expected value # would be "0". - "DecompilerVersion" => "", - "OptimizationUsed" => "1" + "OptimizationUsed" => "true", + "OptimizationRuns" => 200, + "EVMVersion" => "default" } ] @@ -508,9 +488,8 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "ABI" => Jason.encode!(contract_code_info.abi), "ContractName" => contract_code_info.name, "CompilerVersion" => contract_code_info.version, - "DecompiledSourceCode" => "Contract source code not decompiled.", - "DecompilerVersion" => "", - "OptimizationUsed" => "0" + "OptimizationUsed" => "false", + "EVMVersion" => nil } assert response["status"] == "1" @@ -578,9 +557,9 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do contract_source_code assert result["ContractName"] == name - assert result["DecompiledSourceCode"] == "Contract source code not decompiled." - assert result["DecompilerVersion"] == "" - assert result["OptimizationUsed"] == "1" + assert result["DecompiledSourceCode"] == nil + assert result["DecompilerVersion"] == nil + assert result["OptimizationUsed"] == "true" assert :ok = ExJsonSchema.Validator.validate(verify_schema(), response) end end