Proxy for Account abstraction microservice (#9145)
* Proxy for Account abstraction microservice * Process reviewer comments * Fix reviewer comment * Fix response for endpoints with the list * Fix reviewer commentpull/9198/head
parent
9f23f398f6
commit
9c41c6abb6
@ -0,0 +1,176 @@ |
||||
defmodule BlockScoutWeb.API.V2.Proxy.AccountAbstractionController do |
||||
use BlockScoutWeb, :controller |
||||
|
||||
alias BlockScoutWeb.API.V2.Helper |
||||
|
||||
alias Explorer.Chain |
||||
alias Explorer.MicroserviceInterfaces.AccountAbstraction |
||||
|
||||
@address_fields ["bundler", "entry_point", "sender", "address", "factory", "paymaster"] |
||||
|
||||
action_fallback(BlockScoutWeb.API.V2.FallbackController) |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/operations/:user_operation_hash_param` endpoint. |
||||
""" |
||||
@spec operation(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def operation(conn, %{"operation_hash_param" => operation_hash_string}) do |
||||
operation_hash_string |
||||
|> AccountAbstraction.get_user_ops_by_hash() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/bundlers/:address_hash_param` endpoint. |
||||
""" |
||||
@spec bundler(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def bundler(conn, %{"address_hash_param" => address_hash_string}) do |
||||
address_hash_string |
||||
|> AccountAbstraction.get_bundler_by_hash() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/bundlers` endpoint. |
||||
""" |
||||
@spec bundlers(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def bundlers(conn, query_string) do |
||||
query_string |
||||
|> AccountAbstraction.get_bundlers() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/factories/:address_hash_param` endpoint. |
||||
""" |
||||
@spec factory(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def factory(conn, %{"address_hash_param" => address_hash_string}) do |
||||
address_hash_string |
||||
|> AccountAbstraction.get_factory_by_hash() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/factories` endpoint. |
||||
""" |
||||
@spec factories(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def factories(conn, query_string) do |
||||
query_string |
||||
|> AccountAbstraction.get_factories() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/paymasters/:address_hash_param` endpoint. |
||||
""" |
||||
@spec paymaster(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def paymaster(conn, %{"address_hash_param" => address_hash_string}) do |
||||
address_hash_string |
||||
|> AccountAbstraction.get_paymaster_by_hash() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/paymasters` endpoint. |
||||
""" |
||||
@spec paymasters(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def paymasters(conn, query_string) do |
||||
query_string |
||||
|> AccountAbstraction.get_paymasters() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/accounts/:address_hash_param` endpoint. |
||||
""" |
||||
@spec account(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def account(conn, %{"address_hash_param" => address_hash_string}) do |
||||
address_hash_string |
||||
|> AccountAbstraction.get_account_by_hash() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/accounts` endpoint. |
||||
""" |
||||
@spec accounts(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def accounts(conn, query_string) do |
||||
query_string |
||||
|> AccountAbstraction.get_accounts() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/bundles` endpoint. |
||||
""" |
||||
@spec bundles(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def bundles(conn, query_string) do |
||||
query_string |
||||
|> AccountAbstraction.get_bundles() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
@doc """ |
||||
Function to handle GET requests to `/api/v2/proxy/account-abstraction/operations` endpoint. |
||||
""" |
||||
@spec operations(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} |
||||
def operations(conn, query_string) do |
||||
query_string |
||||
|> AccountAbstraction.get_operations() |
||||
|> process_response(conn) |
||||
end |
||||
|
||||
defp extended_info(response) do |
||||
case response do |
||||
%{"items" => items} -> |
||||
extended_items = |
||||
Enum.map(items, fn response_item -> |
||||
add_address_extended_info(response_item) |
||||
end) |
||||
|
||||
response |
||||
|> Map.put("items", extended_items) |
||||
|
||||
_ -> |
||||
add_address_extended_info(response) |
||||
end |
||||
end |
||||
|
||||
defp add_address_extended_info(response) do |
||||
@address_fields |
||||
|> Enum.reduce(response, fn address_output_field, output_response -> |
||||
if Map.has_key?(output_response, address_output_field) do |
||||
output_response |
||||
|> Map.replace( |
||||
address_output_field, |
||||
address_info_from_hash_string(Map.get(output_response, address_output_field)) |
||||
) |
||||
else |
||||
output_response |
||||
end |
||||
end) |
||||
end |
||||
|
||||
defp address_info_from_hash_string(address_hash_string) do |
||||
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), |
||||
{:ok, address} <- Chain.hash_to_address(address_hash, [], false) do |
||||
Helper.address_with_info(address, address_hash_string) |
||||
else |
||||
_ -> address_hash_string |
||||
end |
||||
end |
||||
|
||||
defp process_response(response, conn) do |
||||
case response do |
||||
{:error, :disabled} -> |
||||
conn |
||||
|> put_status(501) |
||||
|> json(extended_info(%{message: "Service is disabled"})) |
||||
|
||||
{status_code, response} -> |
||||
conn |
||||
|> put_status(status_code) |
||||
|> json(extended_info(response)) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,203 @@ |
||||
defmodule Explorer.MicroserviceInterfaces.AccountAbstraction do |
||||
@moduledoc """ |
||||
Interface to interact with Blockscout Account Abstraction (EIP-4337) microservice |
||||
""" |
||||
|
||||
alias Explorer.Utility.Microservice |
||||
alias HTTPoison.Response |
||||
require Logger |
||||
|
||||
@doc """ |
||||
Get user operation by hash via GET {{baseUrl}}/api/v1/userOps/:hash |
||||
""" |
||||
@spec get_user_ops_by_hash(binary()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_user_ops_by_hash(user_operation_hash_string) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
query_params = %{} |
||||
|
||||
http_get_request(operation_by_hash_url(user_operation_hash_string), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get operations list via GET {{baseUrl}}/api/v1/operations |
||||
""" |
||||
@spec get_operations(map()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_operations(query_params) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
http_get_request(operations_url(), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get bundler by address hash via GET {{baseUrl}}/api/v1/bundlers/:address |
||||
""" |
||||
@spec get_bundler_by_hash(binary()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_bundler_by_hash(address_hash_string) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
query_params = %{} |
||||
|
||||
http_get_request(bundler_by_hash_url(address_hash_string), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get bundlers list via GET {{baseUrl}}/api/v1/bundlers |
||||
""" |
||||
@spec get_bundlers(map()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_bundlers(query_params) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
http_get_request(bundlers_url(), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get factory by address hash via GET {{baseUrl}}/api/v1/factories/:address |
||||
""" |
||||
@spec get_factory_by_hash(binary()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_factory_by_hash(address_hash_string) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
query_params = %{} |
||||
|
||||
http_get_request(factory_by_hash_url(address_hash_string), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get factories list via GET {{baseUrl}}/api/v1/factories |
||||
""" |
||||
@spec get_factories(map()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_factories(query_params) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
http_get_request(factories_url(), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get paymaster by address hash via GET {{baseUrl}}/api/v1/paymasters/:address |
||||
""" |
||||
@spec get_paymaster_by_hash(binary()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_paymaster_by_hash(address_hash_string) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
query_params = %{} |
||||
|
||||
http_get_request(paymaster_by_hash_url(address_hash_string), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get paymasters list via GET {{baseUrl}}/api/v1/paymasters |
||||
""" |
||||
@spec get_paymasters(map()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_paymasters(query_params) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
http_get_request(paymasters_url(), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get account by address hash via GET {{baseUrl}}/api/v1/accounts/:address |
||||
""" |
||||
@spec get_account_by_hash(binary()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_account_by_hash(address_hash_string) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
query_params = %{} |
||||
|
||||
http_get_request(account_by_hash_url(address_hash_string), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get accounts list via GET {{baseUrl}}/api/v1/accounts |
||||
""" |
||||
@spec get_accounts(map()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_accounts(query_params) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
http_get_request(accounts_url(), query_params) |
||||
end |
||||
end |
||||
|
||||
@doc """ |
||||
Get bundles list via GET {{baseUrl}}/api/v1/bundles |
||||
""" |
||||
@spec get_bundles(map()) :: {non_neg_integer(), map()} | {:error, :disabled} |
||||
def get_bundles(query_params) do |
||||
with :ok <- Microservice.check_enabled(__MODULE__) do |
||||
http_get_request(bundles_url(), query_params) |
||||
end |
||||
end |
||||
|
||||
defp http_get_request(url, query_params) do |
||||
case HTTPoison.get(url, [], params: query_params) do |
||||
{:ok, %Response{body: body, status_code: 200}} -> |
||||
{:ok, response_json} = Jason.decode(body) |
||||
{200, response_json} |
||||
|
||||
{_, %Response{body: body, status_code: status_code} = error} -> |
||||
old_truncate = Application.get_env(:logger, :truncate) |
||||
Logger.configure(truncate: :infinity) |
||||
|
||||
Logger.error(fn -> |
||||
[ |
||||
"Error while sending request to Account Abstraction microservice url: #{url}: ", |
||||
inspect(error, limit: :infinity, printable_limit: :infinity) |
||||
] |
||||
end) |
||||
|
||||
Logger.configure(truncate: old_truncate) |
||||
{:ok, response_json} = Jason.decode(body) |
||||
{status_code, response_json} |
||||
end |
||||
end |
||||
|
||||
@spec enabled?() :: boolean |
||||
def enabled?, do: Application.get_env(:explorer, __MODULE__)[:enabled] |
||||
|
||||
defp operation_by_hash_url(user_op_hash) do |
||||
"#{base_url()}/userOps/#{user_op_hash}" |
||||
end |
||||
|
||||
defp operations_url do |
||||
"#{base_url()}/userOps" |
||||
end |
||||
|
||||
defp bundler_by_hash_url(address_hash) do |
||||
"#{base_url()}/bundlers/#{address_hash}" |
||||
end |
||||
|
||||
defp bundlers_url do |
||||
"#{base_url()}/bundlers" |
||||
end |
||||
|
||||
defp factory_by_hash_url(address_hash) do |
||||
"#{base_url()}/factories/#{address_hash}" |
||||
end |
||||
|
||||
defp factories_url do |
||||
"#{base_url()}/factories" |
||||
end |
||||
|
||||
defp paymaster_by_hash_url(address_hash) do |
||||
"#{base_url()}/paymasters/#{address_hash}" |
||||
end |
||||
|
||||
defp paymasters_url do |
||||
"#{base_url()}/paymasters" |
||||
end |
||||
|
||||
defp account_by_hash_url(address_hash) do |
||||
"#{base_url()}/accounts/#{address_hash}" |
||||
end |
||||
|
||||
defp accounts_url do |
||||
"#{base_url()}/accounts" |
||||
end |
||||
|
||||
defp bundles_url do |
||||
"#{base_url()}/bundles" |
||||
end |
||||
|
||||
defp base_url do |
||||
"#{Microservice.base_url(__MODULE__)}/api/v1" |
||||
end |
||||
end |
Loading…
Reference in new issue