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