Why: * For API users to be able to get the ABI for a given contract address. Example usage: ``` /api?module=contract&action=getabi&address={addressHash} ``` * Issue link: https://github.com/poanetwork/blockscout/issues/138 This change addresses the need by: * Editing router to support `contract#getabi` API endpoint. * Creating `API.RPC.ContractController.getabi/2` action to process requests to `contract#getabi` endpoint. * Creating `API.RPC.ContractView` to render `contract#getabi` responses. * Adding documentation data for the new `contract#getabi` API endpoint. Documentation data lives in `BlockScoutWeb.Etherscan`pull/696/head
parent
44f4d8f591
commit
a372acaa1d
@ -0,0 +1,40 @@ |
||||
defmodule BlockScoutWeb.API.RPC.ContractController do |
||||
use BlockScoutWeb, :controller |
||||
|
||||
alias Explorer.Chain |
||||
|
||||
def getabi(conn, params) do |
||||
with {:address_param, {:ok, address_param}} <- fetch_address(params), |
||||
{:format, {:ok, address_hash}} <- to_address_hash(address_param), |
||||
{:contract, {:ok, contract}} <- to_smart_contract(address_hash) do |
||||
render(conn, :getabi, %{abi: contract.abi}) |
||||
else |
||||
{:address_param, :error} -> |
||||
render(conn, :error, error: "Query parameter address is required") |
||||
|
||||
{:format, :error} -> |
||||
render(conn, :error, error: "Invalid address hash") |
||||
|
||||
{:contract, :not_found} -> |
||||
render(conn, :error, error: "Contract source code not verified") |
||||
end |
||||
end |
||||
|
||||
defp fetch_address(params) do |
||||
{:address_param, Map.fetch(params, "address")} |
||||
end |
||||
|
||||
defp to_address_hash(address_hash_string) do |
||||
{:format, Chain.string_to_address_hash(address_hash_string)} |
||||
end |
||||
|
||||
defp to_smart_contract(address_hash) do |
||||
result = |
||||
case Chain.address_hash_to_smart_contract(address_hash) do |
||||
nil -> :not_found |
||||
contract -> {:ok, contract} |
||||
end |
||||
|
||||
{:contract, result} |
||||
end |
||||
end |
@ -0,0 +1,13 @@ |
||||
defmodule BlockScoutWeb.API.RPC.ContractView do |
||||
use BlockScoutWeb, :view |
||||
|
||||
alias BlockScoutWeb.API.RPC.RPCView |
||||
|
||||
def render("getabi.json", %{abi: abi}) do |
||||
RPCView.render("show.json", data: Jason.encode!(abi)) |
||||
end |
||||
|
||||
def render("error.json", assigns) do |
||||
RPCView.render("error.json", assigns) |
||||
end |
||||
end |
@ -0,0 +1,76 @@ |
||||
defmodule BlockScoutWeb.API.RPC.ContractControllerTest do |
||||
use BlockScoutWeb.ConnCase |
||||
|
||||
describe "getabi" do |
||||
test "with missing address hash", %{conn: conn} do |
||||
params = %{ |
||||
"module" => "contract", |
||||
"action" => "getabi" |
||||
} |
||||
|
||||
assert response = |
||||
conn |
||||
|> get("/api", params) |
||||
|> json_response(200) |
||||
|
||||
assert response["message"] =~ "address is required" |
||||
assert response["status"] == "0" |
||||
assert Map.has_key?(response, "result") |
||||
refute response["result"] |
||||
end |
||||
|
||||
test "with an invalid address hash", %{conn: conn} do |
||||
params = %{ |
||||
"module" => "contract", |
||||
"action" => "getabi", |
||||
"address" => "badhash" |
||||
} |
||||
|
||||
assert response = |
||||
conn |
||||
|> get("/api", params) |
||||
|> json_response(200) |
||||
|
||||
assert response["message"] =~ "Invalid address hash" |
||||
assert response["status"] == "0" |
||||
assert Map.has_key?(response, "result") |
||||
refute response["result"] |
||||
end |
||||
|
||||
test "with an address that doesn't exist", %{conn: conn} do |
||||
params = %{ |
||||
"module" => "contract", |
||||
"action" => "getabi", |
||||
"address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" |
||||
} |
||||
|
||||
assert response = |
||||
conn |
||||
|> get("/api", params) |
||||
|> json_response(200) |
||||
|
||||
assert response["result"] == nil |
||||
assert response["status"] == "0" |
||||
assert response["message"] == "Contract source code not verified" |
||||
end |
||||
|
||||
test "with a verified contract address", %{conn: conn} do |
||||
contract = insert(:smart_contract) |
||||
|
||||
params = %{ |
||||
"module" => "contract", |
||||
"action" => "getabi", |
||||
"address" => to_string(contract.address_hash) |
||||
} |
||||
|
||||
assert response = |
||||
conn |
||||
|> get("/api", params) |
||||
|> json_response(200) |
||||
|
||||
assert response["result"] == Jason.encode!(contract.abi) |
||||
assert response["status"] == "1" |
||||
assert response["message"] == "OK" |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue