From 9c41c6abb6ec33eeacef68bb49a51d3b1e866df8 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 19 Jan 2024 16:36:38 +0300 Subject: [PATCH] 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 comment --- .github/workflows/config.yml | 26 +-- CHANGELOG.md | 1 + .../lib/block_scout_web/api_router.ex | 14 ++ .../proxy/account_abstraction_controller.ex | 176 +++++++++++++++ .../block_scout_web/views/api/v2/helper.ex | 14 +- .../account_abstraction.ex | 203 ++++++++++++++++++ config/runtime.exs | 12 +- docker-compose/envs/common-blockscout.env | 2 + 8 files changed, 426 insertions(+), 22 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex create mode 100644 apps/explorer/lib/explorer/microservice_interfaces/account_abstraction.ex diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index f07143f50d..80ffe70c0b 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -75,7 +75,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps- @@ -133,7 +133,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -157,7 +157,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -186,7 +186,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -230,7 +230,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -256,7 +256,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -285,7 +285,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -333,7 +333,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -379,7 +379,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -441,7 +441,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -501,7 +501,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -572,7 +572,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -640,7 +640,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_34-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_35-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d7412021c..9bfe2885d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - [#9155](https://github.com/blockscout/blockscout/pull/9155) - Allow bypassing avg block time in proxy implementation re-fetch ttl calculation +- [#9145](https://github.com/blockscout/blockscout/pull/9145) - Proxy for Account abstraction microservice - [#9131](https://github.com/blockscout/blockscout/pull/9131) - Merge addresses stage with address referencing - [#9072](https://github.com/blockscout/blockscout/pull/9072) - Add tracing by block logic for geth - [#9056](https://github.com/blockscout/blockscout/pull/9056) - Noves.fi API proxy diff --git a/apps/block_scout_web/lib/block_scout_web/api_router.ex b/apps/block_scout_web/lib/block_scout_web/api_router.ex index 5f8415014a..d029916e2d 100644 --- a/apps/block_scout_web/lib/block_scout_web/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/api_router.ex @@ -307,6 +307,20 @@ defmodule BlockScoutWeb.ApiRouter do get("/transactions/:transaction_hash_param/describe", V2.Proxy.NovesFiController, :describe_transaction) get("/addresses/:address_hash_param/transactions", V2.Proxy.NovesFiController, :address_transactions) end + + scope "/account-abstraction" do + get("/operations/:operation_hash_param", V2.Proxy.AccountAbstractionController, :operation) + get("/bundlers/:address_hash_param", V2.Proxy.AccountAbstractionController, :bundler) + get("/bundlers", V2.Proxy.AccountAbstractionController, :bundlers) + get("/factories/:address_hash_param", V2.Proxy.AccountAbstractionController, :factory) + get("/factories", V2.Proxy.AccountAbstractionController, :factories) + get("/paymasters/:address_hash_param", V2.Proxy.AccountAbstractionController, :paymaster) + get("/paymasters", V2.Proxy.AccountAbstractionController, :paymasters) + get("/accounts/:address_hash_param", V2.Proxy.AccountAbstractionController, :account) + get("/accounts", V2.Proxy.AccountAbstractionController, :accounts) + get("/bundles", V2.Proxy.AccountAbstractionController, :bundles) + get("/operations", V2.Proxy.AccountAbstractionController, :operations) + end end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex new file mode 100644 index 0000000000..89fb25900e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex @@ -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 diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index 96a69840ee..010171545d 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -48,7 +48,11 @@ defmodule BlockScoutWeb.API.V2.Helper do }) end - defp address_with_info(%Address{} = address, _address_hash) do + @doc """ + Gets address with the additional info for api v2 + """ + @spec address_with_info(any(), any()) :: nil | %{optional(<<_::32, _::_*8>>) => any()} + def address_with_info(%Address{} = address, _address_hash) do %{ "hash" => Address.checksum(address), "is_contract" => Address.is_smart_contract(address), @@ -59,21 +63,21 @@ defmodule BlockScoutWeb.API.V2.Helper do } end - defp address_with_info(%{ens_domain_name: name}, address_hash) do + def address_with_info(%{ens_domain_name: name}, address_hash) do nil |> address_with_info(address_hash) |> Map.put("ens_domain_name", name) end - defp address_with_info(%NotLoaded{}, address_hash) do + def address_with_info(%NotLoaded{}, address_hash) do address_with_info(nil, address_hash) end - defp address_with_info(nil, nil) do + def address_with_info(nil, nil) do nil end - defp address_with_info(_, address_hash) do + def address_with_info(_, address_hash) do %{ "hash" => Address.checksum(address_hash), "is_contract" => false, diff --git a/apps/explorer/lib/explorer/microservice_interfaces/account_abstraction.ex b/apps/explorer/lib/explorer/microservice_interfaces/account_abstraction.ex new file mode 100644 index 0000000000..5dbbf2d945 --- /dev/null +++ b/apps/explorer/lib/explorer/microservice_interfaces/account_abstraction.ex @@ -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 diff --git a/config/runtime.exs b/config/runtime.exs index c81ceeafbb..624910593b 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -405,6 +405,14 @@ config :explorer, Explorer.SmartContract.SigProviderInterface, service_url: System.get_env("MICROSERVICE_SIG_PROVIDER_URL"), enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_SIG_PROVIDER_ENABLED") +config :explorer, Explorer.MicroserviceInterfaces.BENS, + service_url: System.get_env("MICROSERVICE_BENS_URL"), + enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_BENS_ENABLED") + +config :explorer, Explorer.MicroserviceInterfaces.AccountAbstraction, + service_url: System.get_env("MICROSERVICE_ACCOUNT_ABSTRACTION_URL"), + enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED") + config :explorer, Explorer.ThirdPartyIntegrations.AirTable, table_url: System.get_env("ACCOUNT_PUBLIC_TAGS_AIRTABLE_URL"), api_key: System.get_env("ACCOUNT_PUBLIC_TAGS_AIRTABLE_API_KEY") @@ -461,10 +469,6 @@ config :explorer, Explorer.Chain.Transaction, config :explorer, Explorer.Chain.Cache.AddressesTabsCounters, ttl: ConfigHelper.parse_time_env_var("ADDRESSES_TABS_COUNTERS_TTL", "10m") -config :explorer, Explorer.MicroserviceInterfaces.BENS, - service_url: System.get_env("MICROSERVICE_BENS_URL"), - enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_BENS_ENABLED") - config :explorer, Explorer.Migrator.TransactionsDenormalization, batch_size: ConfigHelper.parse_integer_env_var("DENORMALIZATION_MIGRATION_BATCH_SIZE", 500), concurrency: ConfigHelper.parse_integer_env_var("DENORMALIZATION_MIGRATION_CONCURRENCY", 10) diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index b12e37cac1..d355548e60 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -238,6 +238,8 @@ MICROSERVICE_SIG_PROVIDER_ENABLED=true MICROSERVICE_SIG_PROVIDER_URL=http://sig-provider:8050/ # MICROSERVICE_BENS_URL= # MICROSERVICE_BENS_ENABLED= +#MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED=true +#MICROSERVICE_ACCOUNT_ABSTRACTION_URL= DECODE_NOT_A_CONTRACT_CALLS=true # DATABASE_READ_ONLY_API_URL= # ACCOUNT_DATABASE_URL=