fix: `getsourcecode` in API v1 for verified proxy (#10273)

pull/10280/head
Fedor Ivanov 5 months ago committed by GitHub
parent ed5e3e36c0
commit fdfea14e9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
  2. 222
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs

@ -78,8 +78,13 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
defp set_proxy_info(contract_output, contract) do
result =
if contract.is_proxy do
implementation_address_hash_string = List.first(contract.implementation_address_hash_strings)
# todo: `ImplementationAddress` is kept for backward compatibility,
# remove when clients unbound from these props
contract_output
|> Map.put_new(:ImplementationAddress, contract.implementation_address_hash_string)
|> Map.put_new(:ImplementationAddress, implementation_address_hash_string)
|> Map.put_new(:ImplementationAddresses, contract.implementation_address_hash_strings)
else
contract_output
end

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
use BlockScoutWeb.ConnCase
alias Explorer.Chain.SmartContract
alias Explorer.TestHelper
alias Explorer.{Chain, TestHelper}
alias Explorer.Chain.{Address, SmartContract}
import Mox
@ -539,6 +539,224 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response)
end
test "with a verified proxy contract address", %{conn: conn} do
implementation_contract =
insert(:smart_contract,
name: "Implementation",
external_libraries: [],
constructor_arguments: "",
abi: [
%{
"type" => "constructor",
"inputs" => [
%{"type" => "address", "name" => "_proxyStorage"},
%{"type" => "address", "name" => "_implementationAddress"}
]
},
%{
"constant" => false,
"inputs" => [%{"name" => "x", "type" => "uint256"}],
"name" => "set",
"outputs" => [],
"payable" => false,
"stateMutability" => "nonpayable",
"type" => "function"
},
%{
"constant" => true,
"inputs" => [],
"name" => "get",
"outputs" => [%{"name" => "", "type" => "uint256"}],
"payable" => false,
"stateMutability" => "view",
"type" => "function"
}
],
license_type: 9
)
implementation_contract_address_hash_string =
Base.encode16(implementation_contract.address_hash.bytes, case: :lower)
proxy_tx_input =
"0x11b804ab000000000000000000000000" <>
implementation_contract_address_hash_string <>
"000000000000000000000000000000000000000000000000000000000000006035323031313537360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000284e159163400000000000000000000000034420c13696f4ac650b9fafe915553a1abcd7dd30000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000ff5ae9b0a7522736299d797d80b8fc6f31d61100000000000000000000000000ff5ae9b0a7522736299d797d80b8fc6f31d6110000000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034420c13696f4ac650b9fafe915553a1abcd7dd300000000000000000000000000000000000000000000000000000000000000184f7074696d69736d2053756273637269626572204e465473000000000000000000000000000000000000000000000000000000000000000000000000000000054f504e46540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d66544e504839765651334b5952346d6b52325a6b757756424266456f5a5554545064395538666931503332752f300000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c82bbe41f2cf04e3a8efa18f7032bdd7f6d98a81000000000000000000000000efba8a2a82ec1fb1273806174f5e28fbb917cf9500000000000000000000000000000000000000000000000000000000"
proxy_deployed_bytecode =
"0x363d3d373d3d3d363d73" <> implementation_contract_address_hash_string <> "5af43d82803e903d91602b57fd5bf3"
proxy_address =
insert(:contract_address,
contract_code: proxy_deployed_bytecode,
verified: true
)
proxy_abi = [
%{
"type" => "function",
"stateMutability" => "nonpayable",
"payable" => false,
"outputs" => [%{"type" => "bool", "name" => ""}],
"name" => "upgradeTo",
"inputs" => [%{"type" => "address", "name" => "newImplementation"}],
"constant" => false
},
%{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "uint256", "name" => ""}],
"name" => "version",
"inputs" => [],
"constant" => true
},
%{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "address", "name" => ""}],
"name" => "implementation",
"inputs" => [],
"constant" => true
},
%{
"type" => "function",
"stateMutability" => "nonpayable",
"payable" => false,
"outputs" => [],
"name" => "renounceOwnership",
"inputs" => [],
"constant" => false
},
%{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "address", "name" => ""}],
"name" => "getOwner",
"inputs" => [],
"constant" => true
},
%{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "address", "name" => ""}],
"name" => "getProxyStorage",
"inputs" => [],
"constant" => true
},
%{
"type" => "function",
"stateMutability" => "nonpayable",
"payable" => false,
"outputs" => [],
"name" => "transferOwnership",
"inputs" => [%{"type" => "address", "name" => "_newOwner"}],
"constant" => false
},
%{
"type" => "constructor",
"stateMutability" => "nonpayable",
"payable" => false,
"inputs" => [
%{"type" => "address", "name" => "_proxyStorage"},
%{"type" => "address", "name" => "_implementationAddress"}
]
},
%{"type" => "fallback", "stateMutability" => "nonpayable", "payable" => false},
%{
"type" => "event",
"name" => "Upgraded",
"inputs" => [
%{"type" => "uint256", "name" => "version", "indexed" => false},
%{"type" => "address", "name" => "implementation", "indexed" => true}
],
"anonymous" => false
},
%{
"type" => "event",
"name" => "OwnershipRenounced",
"inputs" => [%{"type" => "address", "name" => "previousOwner", "indexed" => true}],
"anonymous" => false
},
%{
"type" => "event",
"name" => "OwnershipTransferred",
"inputs" => [
%{"type" => "address", "name" => "previousOwner", "indexed" => true},
%{"type" => "address", "name" => "newOwner", "indexed" => true}
],
"anonymous" => false
}
]
proxy_contract =
insert(:smart_contract,
address_hash: proxy_address.hash,
name: "Proxy",
abi: proxy_abi
)
tx =
insert(:transaction,
created_contract_address_hash: proxy_address.hash,
input: proxy_tx_input
)
|> with_block(status: :ok)
name = implementation_contract.name
from = Address.checksum(tx.from_address_hash)
tx_hash = to_string(tx.hash)
address_hash = Address.checksum(proxy_address.hash)
{:ok, implementation_contract_address_hash} =
Chain.string_to_address_hash("0x" <> implementation_contract_address_hash_string)
checksummed_implementation_contract_address_hash =
implementation_contract_address_hash && Address.checksum(implementation_contract_address_hash)
insert(:proxy_implementation,
proxy_address_hash: proxy_address.hash,
proxy_type: "eip1167",
address_hashes: [implementation_contract.address_hash],
names: [name]
)
params = %{
"module" => "contract",
"action" => "getsourcecode",
"address" => Address.checksum(proxy_address.hash)
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
expected_result = [
%{
"Address" => to_string(proxy_contract.address_hash),
"SourceCode" => proxy_contract.contract_source_code,
"ABI" => Jason.encode!(proxy_contract.abi),
"ContractName" => proxy_contract.name,
"CompilerVersion" => proxy_contract.compiler_version,
"FileName" => "",
"IsProxy" => "true",
"ImplementationAddress" => to_string(implementation_contract.address_hash),
"ImplementationAddresses" => [to_string(implementation_contract.address_hash)],
"EVMVersion" => nil,
"OptimizationUsed" => "false"
}
]
assert response["result"] == expected_result
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response)
end
test "with constructor arguments", %{conn: conn} do
contract =
insert(:smart_contract,

Loading…
Cancel
Save