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