diff --git a/apps/explorer_web/lib/explorer_web/controllers/api/rpc/address_controller.ex b/apps/explorer_web/lib/explorer_web/controllers/api/rpc/address_controller.ex new file mode 100644 index 0000000000..35044f2996 --- /dev/null +++ b/apps/explorer_web/lib/explorer_web/controllers/api/rpc/address_controller.ex @@ -0,0 +1,42 @@ +defmodule ExplorerWeb.API.RPC.AddressController do + use ExplorerWeb, :controller + + alias Explorer.Chain + alias Explorer.Chain.{Address, Wei} + + def balance(conn, params) do + with {:address_param, {:ok, address_hash_string}} <- fetch_address(params), + {:format, {:ok, address_hash}} <- to_address_hash(address_hash_string), + {:ok, address} <- hash_to_address(address_hash) do + render(conn, :balance, %{address: address}) + else + {:address_param, :error} -> + conn + |> put_status(400) + |> render(:error, error: "Query parameter 'address' is required") + + {:format, :error} -> + conn + |> put_status(400) + |> render(:error, error: "Invalid address hash") + 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 hash_to_address(address_hash) do + address = + case Chain.hash_to_address(address_hash) do + {:ok, address} -> address + {:error, :not_found} -> %Address{fetched_balance: %Wei{value: 0}} + end + + {:ok, address} + end +end diff --git a/apps/explorer_web/lib/explorer_web/router.ex b/apps/explorer_web/lib/explorer_web/router.ex index a1bf5713ff..c2e519af67 100644 --- a/apps/explorer_web/lib/explorer_web/router.ex +++ b/apps/explorer_web/lib/explorer_web/router.ex @@ -39,7 +39,8 @@ defmodule ExplorerWeb.Router do alias ExplorerWeb.API.RPC forward("/", RPCTranslator, %{ - "block" => RPC.BlockController + "block" => RPC.BlockController, + "account" => RPC.AddressController }) end diff --git a/apps/explorer_web/lib/explorer_web/views/api/rpc/address_view.ex b/apps/explorer_web/lib/explorer_web/views/api/rpc/address_view.ex new file mode 100644 index 0000000000..cdd3236872 --- /dev/null +++ b/apps/explorer_web/lib/explorer_web/views/api/rpc/address_view.ex @@ -0,0 +1,15 @@ +defmodule ExplorerWeb.API.RPC.AddressView do + use ExplorerWeb, :view + + alias ExplorerWeb.API.RPC.RPCView + + def render("balance.json", %{address: address}) do + ether_balance = format_wei_value(address.fetched_balance, :ether, include_unit_label: false) + data = ether_balance + RPCView.render("show.json", data: data) + end + + def render("error.json", %{error: error}) do + RPCView.render("error.json", error: error) + end +end diff --git a/apps/explorer_web/test/explorer_web/controllers/api/rpc/address_controller_test.exs b/apps/explorer_web/test/explorer_web/controllers/api/rpc/address_controller_test.exs new file mode 100644 index 0000000000..88d1f6bbe3 --- /dev/null +++ b/apps/explorer_web/test/explorer_web/controllers/api/rpc/address_controller_test.exs @@ -0,0 +1,83 @@ +defmodule ExplorerWeb.API.RPC.AddressControllerTest do + use ExplorerWeb.ConnCase + + alias Explorer.Chain.Wei + + describe "balance" do + test "with missing address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "balance" + } + + assert response = + conn + |> get("/api", params) + |> json_response(400) + + 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" => "account", + "action" => "balance", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(400) + + 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" => "account", + "action" => "balance", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == "0" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a valid address", %{conn: conn} do + address = insert(:address, fetched_balance: 100) + + params = %{ + "module" => "account", + "action" => "balance", + "address" => "#{address.hash}" + } + + expected_balance = + address.fetched_balance + |> Wei.to(:ether) + |> Decimal.to_string(:normal) + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_balance + assert response["status"] == "1" + assert response["message"] == "OK" + end + end +end