feat: Integrate Metadata microservice (#9706)

* feat: Integrate Metadata microservice

* Fix formatting
pull/9812/head
nikitosing 8 months ago committed by GitHub
parent 36df683929
commit 45fa2aab5a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 29
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex
  2. 16
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex
  3. 7
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex
  4. 4
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex
  5. 9
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex
  6. 35
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex
  7. 6
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex
  8. 20
      apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex
  9. 6
      apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs
  10. 3
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs
  11. 1
      apps/explorer/lib/explorer/chain/address.ex
  12. 303
      apps/explorer/lib/explorer/chain/address/metadata_preloader.ex
  13. 308
      apps/explorer/lib/explorer/microservice_interfaces/bens.ex
  14. 93
      apps/explorer/lib/explorer/microservice_interfaces/metadata.ex
  15. 4
      config/runtime.exs
  16. 2
      docker-compose/envs/common-blockscout.env

@ -21,6 +21,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
] ]
import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1, maybe_preload_ens_to_address: 1] import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1, maybe_preload_ens_to_address: 1]
import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1]
alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.AccessHelper
alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView} alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView}
@ -152,7 +153,10 @@ defmodule BlockScoutWeb.API.V2.AddressController do
conn conn
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:transactions, %{transactions: transactions |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:transactions, %{
transactions: transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
end end
@ -198,7 +202,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:token_transfers, %{ |> render(:token_transfers, %{
token_transfers: token_transfers |> maybe_preload_ens(), token_transfers: token_transfers |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params next_page_params: next_page_params
}) })
end end
@ -230,7 +234,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:token_transfers, %{ |> render(:token_transfers, %{
token_transfers: token_transfers |> maybe_preload_ens(), token_transfers: token_transfers |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params next_page_params: next_page_params
}) })
end end
@ -263,7 +267,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:internal_transactions, %{ |> render(:internal_transactions, %{
internal_transactions: internal_transactions |> maybe_preload_ens(), internal_transactions: internal_transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params next_page_params: next_page_params
}) })
end end
@ -286,7 +290,10 @@ defmodule BlockScoutWeb.API.V2.AddressController do
conn conn
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:logs, %{logs: logs |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:logs, %{
logs: logs |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
end end
@ -303,7 +310,10 @@ defmodule BlockScoutWeb.API.V2.AddressController do
conn conn
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:logs, %{logs: logs |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:logs, %{
logs: logs |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
end end
@ -405,7 +415,10 @@ defmodule BlockScoutWeb.API.V2.AddressController do
conn conn
|> put_status(200) |> put_status(200)
|> put_view(WithdrawalView) |> put_view(WithdrawalView)
|> render(:withdrawals, %{withdrawals: withdrawals |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:withdrawals, %{
withdrawals: withdrawals |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
end end
@ -425,7 +438,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:addresses, %{ |> render(:addresses, %{
addresses: addresses |> maybe_preload_ens(), addresses: addresses |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params, next_page_params: next_page_params,
exchange_rate: exchange_rate, exchange_rate: exchange_rate,
total_supply: total_supply total_supply: total_supply

@ -14,6 +14,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do
only: [delete_parameters_from_next_page_params: 1, select_block_type: 1, type_filter_options: 1] only: [delete_parameters_from_next_page_params: 1, select_block_type: 1, type_filter_options: 1]
import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1]
import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1]
alias BlockScoutWeb.API.V2.{TransactionView, WithdrawalView} alias BlockScoutWeb.API.V2.{TransactionView, WithdrawalView}
alias Explorer.Chain alias Explorer.Chain
@ -125,7 +126,10 @@ defmodule BlockScoutWeb.API.V2.BlockController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:blocks, %{blocks: blocks |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:blocks, %{
blocks: blocks |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
def transactions(conn, %{"block_hash_or_number" => block_hash_or_number} = params) do def transactions(conn, %{"block_hash_or_number" => block_hash_or_number} = params) do
@ -148,7 +152,10 @@ defmodule BlockScoutWeb.API.V2.BlockController do
conn conn
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:transactions, %{transactions: transactions |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:transactions, %{
transactions: transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
end end
@ -167,7 +174,10 @@ defmodule BlockScoutWeb.API.V2.BlockController do
conn conn
|> put_status(200) |> put_status(200)
|> put_view(WithdrawalView) |> put_view(WithdrawalView)
|> render(:withdrawals, %{withdrawals: withdrawals |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:withdrawals, %{
withdrawals: withdrawals |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
end end
end end

@ -8,6 +8,7 @@ defmodule BlockScoutWeb.API.V2.MainPageController do
import BlockScoutWeb.Account.AuthController, only: [current_user: 1] import BlockScoutWeb.Account.AuthController, only: [current_user: 1]
import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1]
import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1]
@transactions_options [ @transactions_options [
necessity_by_association: %{ necessity_by_association: %{
@ -34,7 +35,7 @@ defmodule BlockScoutWeb.API.V2.MainPageController do
conn conn
|> put_status(200) |> put_status(200)
|> put_view(BlockView) |> put_view(BlockView)
|> render(:blocks, %{blocks: blocks |> maybe_preload_ens()}) |> render(:blocks, %{blocks: blocks |> maybe_preload_ens() |> maybe_preload_metadata()})
end end
def optimism_deposits(conn, _params) do def optimism_deposits(conn, _params) do
@ -56,7 +57,7 @@ defmodule BlockScoutWeb.API.V2.MainPageController do
conn conn
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:transactions, %{transactions: recent_transactions |> maybe_preload_ens()}) |> render(:transactions, %{transactions: recent_transactions |> maybe_preload_ens() |> maybe_preload_metadata()})
end end
def watchlist_transactions(conn, _params) do def watchlist_transactions(conn, _params) do
@ -67,7 +68,7 @@ defmodule BlockScoutWeb.API.V2.MainPageController do
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:transactions_watchlist, %{ |> render(:transactions_watchlist, %{
transactions: transactions |> maybe_preload_ens(), transactions: transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
watchlist_names: watchlist_names watchlist_names: watchlist_names
}) })
end end

@ -177,7 +177,7 @@ defmodule BlockScoutWeb.API.V2.Proxy.AccountAbstractionController do
end end
address_hash_strings address_hash_strings
|> Enum.filter(&(!is_nil(&1))) |> Enum.reject(&is_nil/1)
|> Enum.uniq() |> Enum.uniq()
|> Enum.map(fn hash_string -> |> Enum.map(fn hash_string ->
case Chain.string_to_address_hash(hash_string) do case Chain.string_to_address_hash(hash_string) do
@ -185,7 +185,7 @@ defmodule BlockScoutWeb.API.V2.Proxy.AccountAbstractionController do
_ -> nil _ -> nil
end end
end) end)
|> Enum.filter(&(!is_nil(&1))) |> Enum.reject(&is_nil/1)
end end
defp replace_address_hashes(response, addresses) do defp replace_address_hashes(response, addresses) do

@ -26,6 +26,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do
] ]
import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1]
import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1]
action_fallback(BlockScoutWeb.API.V2.FallbackController) action_fallback(BlockScoutWeb.API.V2.FallbackController)
@ -96,7 +97,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:token_transfers, %{ |> render(:token_transfers, %{
token_transfers: token_transfers |> maybe_preload_ens(), token_transfers: token_transfers |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params next_page_params: next_page_params
}) })
end end
@ -116,7 +117,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:token_balances, %{ |> render(:token_balances, %{
token_balances: token_balances |> maybe_preload_ens(), token_balances: token_balances |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params, next_page_params: next_page_params,
token: token token: token
}) })
@ -239,7 +240,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do
|> put_status(200) |> put_status(200)
|> put_view(TransactionView) |> put_view(TransactionView)
|> render(:token_transfers, %{ |> render(:token_transfers, %{
token_transfers: token_transfers |> maybe_preload_ens(), token_transfers: token_transfers |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params next_page_params: next_page_params
}) })
end end
@ -269,7 +270,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:token_balances, %{ |> render(:token_balances, %{
token_balances: token_holders |> maybe_preload_ens(), token_balances: token_holders |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params, next_page_params: next_page_params,
token: token token: token
}) })

@ -25,6 +25,9 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1, maybe_preload_ens_to_transaction: 1] import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1, maybe_preload_ens_to_transaction: 1]
import Explorer.MicroserviceInterfaces.Metadata,
only: [maybe_preload_metadata: 1, maybe_preload_metadata_to_transaction: 1]
alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.AccessHelper
alias BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation, as: TransactionInterpretationService alias BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation, as: TransactionInterpretationService
alias BlockScoutWeb.Models.TransactionStateHelper alias BlockScoutWeb.Models.TransactionStateHelper
@ -130,7 +133,9 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
Chain.preload_token_transfers(transaction, @token_transfers_in_tx_necessity_by_association, @api_true, false) do Chain.preload_token_transfers(transaction, @token_transfers_in_tx_necessity_by_association, @api_true, false) do
conn conn
|> put_status(200) |> put_status(200)
|> render(:transaction, %{transaction: preloaded |> maybe_preload_ens_to_transaction()}) |> render(:transaction, %{
transaction: preloaded |> maybe_preload_ens_to_transaction() |> maybe_preload_metadata_to_transaction()
})
end end
end end
@ -158,7 +163,10 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:transactions, %{transactions: transactions |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:transactions, %{
transactions: transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
@doc """ @doc """
@ -175,7 +183,10 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:transactions, %{transactions: transactions |> maybe_preload_ens(), items: true}) |> render(:transactions, %{
transactions: transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
items: true
})
end end
@doc """ @doc """
@ -206,7 +217,10 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:transactions, %{transactions: transactions |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:transactions, %{
transactions: transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
def execution_node(conn, %{"execution_node_hash_param" => execution_node_hash_string} = params) do def execution_node(conn, %{"execution_node_hash_param" => execution_node_hash_string} = params) do
@ -226,7 +240,10 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:transactions, %{transactions: transactions |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:transactions, %{
transactions: transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
end end
@ -288,7 +305,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:token_transfers, %{ |> render(:token_transfers, %{
token_transfers: token_transfers |> maybe_preload_ens(), token_transfers: token_transfers |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params next_page_params: next_page_params
}) })
end end
@ -316,7 +333,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:internal_transactions, %{ |> render(:internal_transactions, %{
internal_transactions: internal_transactions |> maybe_preload_ens(), internal_transactions: internal_transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params next_page_params: next_page_params
}) })
end end
@ -351,7 +368,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
|> put_status(200) |> put_status(200)
|> render(:logs, %{ |> render(:logs, %{
tx_hash: transaction_hash, tx_hash: transaction_hash,
logs: logs |> maybe_preload_ens(), logs: logs |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params next_page_params: next_page_params
}) })
end end
@ -405,7 +422,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:transactions_watchlist, %{ |> render(:transactions_watchlist, %{
transactions: transactions |> maybe_preload_ens(), transactions: transactions |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params, next_page_params: next_page_params,
watchlist_names: watchlist_names watchlist_names: watchlist_names
}) })

@ -6,6 +6,7 @@ defmodule BlockScoutWeb.API.V2.WithdrawalController do
import BlockScoutWeb.PagingHelper, only: [delete_parameters_from_next_page_params: 1] import BlockScoutWeb.PagingHelper, only: [delete_parameters_from_next_page_params: 1]
import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1]
import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1]
alias Explorer.Chain alias Explorer.Chain
@ -21,7 +22,10 @@ defmodule BlockScoutWeb.API.V2.WithdrawalController do
conn conn
|> put_status(200) |> put_status(200)
|> render(:withdrawals, %{withdrawals: withdrawals |> maybe_preload_ens(), next_page_params: next_page_params}) |> render(:withdrawals, %{
withdrawals: withdrawals |> maybe_preload_ens() |> maybe_preload_metadata(),
next_page_params: next_page_params
})
end end
def withdrawals_counters(conn, _params) do def withdrawals_counters(conn, _params) do

@ -59,20 +59,22 @@ defmodule BlockScoutWeb.API.V2.Helper do
"name" => address_name(address), "name" => address_name(address),
"implementation_name" => implementation_name(address), "implementation_name" => implementation_name(address),
"is_verified" => verified?(address), "is_verified" => verified?(address),
"ens_domain_name" => address.ens_domain_name "ens_domain_name" => address.ens_domain_name,
"metadata" => address.metadata
} }
end end
def address_with_info(%{ens_domain_name: name}, address_hash) do
nil
|> address_with_info(address_hash)
|> Map.put("ens_domain_name", name)
end
def address_with_info(%NotLoaded{}, address_hash) do def address_with_info(%NotLoaded{}, address_hash) do
address_with_info(nil, address_hash) address_with_info(nil, address_hash)
end end
def address_with_info(address_info, address_hash) when is_map(address_info) do
nil
|> address_with_info(address_hash)
|> Map.put("ens_domain_name", address_info[:ens_domain_name])
|> Map.put("metadata", address_info[:metadata])
end
def address_with_info(nil, nil) do def address_with_info(nil, nil) do
nil nil
end end
@ -83,7 +85,9 @@ defmodule BlockScoutWeb.API.V2.Helper do
"is_contract" => false, "is_contract" => false,
"name" => nil, "name" => nil,
"implementation_name" => nil, "implementation_name" => nil,
"is_verified" => nil "is_verified" => nil,
"ens_domain_name" => nil,
"metadata" => nil
} }
end end

@ -158,7 +158,8 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do
"private_tags" => [], "private_tags" => [],
"public_tags" => [], "public_tags" => [],
"watchlist_names" => [], "watchlist_names" => [],
"ens_domain_name" => nil "ens_domain_name" => nil,
"metadata" => nil
} }
}} }}
end) end)
@ -211,7 +212,8 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do
"private_tags" => [], "private_tags" => [],
"public_tags" => [], "public_tags" => [],
"watchlist_names" => [], "watchlist_names" => [],
"ens_domain_name" => nil "ens_domain_name" => nil,
"metadata" => nil
} }
}} }}
end) end)

@ -87,7 +87,8 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
"has_token_transfers" => false, "has_token_transfers" => false,
"watchlist_address_id" => nil, "watchlist_address_id" => nil,
"has_beacon_chain_withdrawals" => false, "has_beacon_chain_withdrawals" => false,
"ens_domain_name" => nil "ens_domain_name" => nil,
"metadata" => nil
} }
request = get(conn, "/api/v2/addresses/#{Address.checksum(address.hash)}") request = get(conn, "/api/v2/addresses/#{Address.checksum(address.hash)}")

@ -91,6 +91,7 @@ defmodule Explorer.Chain.Address do
field(:token_transfers_count, :integer) field(:token_transfers_count, :integer)
field(:gas_used, :integer) field(:gas_used, :integer)
field(:ens_domain_name, :string, virtual: true) field(:ens_domain_name, :string, virtual: true)
field(:metadata, :any, virtual: true)
has_one(:smart_contract, SmartContract, references: :hash) has_one(:smart_contract, SmartContract, references: :hash)
has_one(:token, Token, foreign_key: :contract_address_hash, references: :hash) has_one(:token, Token, foreign_key: :contract_address_hash, references: :hash)

@ -0,0 +1,303 @@
defmodule Explorer.Chain.Address.MetadataPreloader do
@moduledoc """
Module responsible for preloading metadata (from BENS, Metadata microservices) to addresses.
"""
alias Ecto.Association.NotLoaded
alias Explorer.MicroserviceInterfaces.{BENS, Metadata}
alias Explorer.Chain.{
Address,
Address.CurrentTokenBalance,
Block,
InternalTransaction,
Log,
TokenTransfer,
Transaction,
Withdrawal
}
@type supported_types ::
Address.t()
| Block.t()
| CurrentTokenBalance.t()
| InternalTransaction.t()
| Log.t()
| TokenTransfer.t()
| Transaction.t()
| Withdrawal.t()
@type supported_input :: [supported_types] | supported_types
@doc """
Preloads ENS/metadata to supported entities
"""
@spec maybe_preload_meta(supported_input, module(), (supported_input -> supported_input)) :: supported_input
def maybe_preload_meta(argument, module, function \\ &preload_ens_to_list/1) do
if module.enabled?() do
function.(argument)
else
argument
end
end
@doc """
Preloads ENS name to Transaction.t()
"""
@spec preload_ens_to_transaction(Transaction.t()) :: Transaction.t()
def preload_ens_to_transaction(transaction) do
[transaction_with_ens] = preload_ens_to_list([transaction])
transaction_with_ens
end
@doc """
Preloads ENS name to Address.t()
"""
@spec preload_ens_to_address(Address.t()) :: Address.t()
def preload_ens_to_address(address) do
[address_with_ens] = preload_ens_to_list([address])
address_with_ens
end
@doc """
Preloads ENS names to list of supported entities
"""
@spec preload_ens_to_list([supported_types]) :: [supported_types]
def preload_ens_to_list(items) do
address_hash_strings =
items
|> Enum.reduce([], fn item, acc ->
item_to_address_hash_strings(item) ++ acc
end)
|> Enum.uniq()
case BENS.ens_names_batch_request(address_hash_strings) do
{:ok, result} ->
put_ens_names(result["names"], items)
_ ->
items
end
end
@doc """
Preloads metadata to list of supported entities
"""
@spec preload_metadata_to_list([supported_types]) :: [supported_types]
def preload_metadata_to_list(items) do
address_hash_strings =
items
|> Enum.reduce([], fn item, acc ->
item_to_address_hash_strings(item) ++ acc
end)
|> Enum.uniq()
case Metadata.get_addresses_tags(address_hash_strings) do
{:ok, result} ->
put_metadata(result["addresses"], items)
_ ->
items
end
end
@doc """
Preloads metadata to Transaction.t()
"""
@spec preload_metadata_to_transaction(Transaction.t()) :: Transaction.t()
def preload_metadata_to_transaction(transaction) do
[transaction_with_metadata] = preload_metadata_to_list([transaction])
transaction_with_metadata
end
@doc """
Preload ENS info to search result, using get_address/1
"""
@spec preload_ens_info_to_search_results(list) :: list
def preload_ens_info_to_search_results(list) do
Enum.map(list, fn
%{type: "address", ens_info: ens_info} = search_result when not is_nil(ens_info) ->
search_result
%{type: "address"} = search_result ->
ens_info = search_result[:address_hash] |> BENS.get_address()
Map.put(search_result, :ens_info, ens_info)
search_result ->
search_result
end)
end
defp item_to_address_hash_strings(%Transaction{
to_address_hash: to_address_hash,
created_contract_address_hash: created_contract_address_hash,
from_address_hash: from_address_hash,
token_transfers: token_transfers
}) do
token_transfers_addresses =
case token_transfers do
token_transfers_list when is_list(token_transfers_list) ->
List.flatten(Enum.map(token_transfers_list, &item_to_address_hash_strings/1))
_ ->
[]
end
([to_address_hash, created_contract_address_hash, from_address_hash]
|> Enum.reject(&is_nil/1)
|> Enum.map(&to_string/1)) ++ token_transfers_addresses
end
defp item_to_address_hash_strings(%TokenTransfer{
to_address_hash: to_address_hash,
from_address_hash: from_address_hash
}) do
[to_string(to_address_hash), to_string(from_address_hash)]
end
defp item_to_address_hash_strings(%InternalTransaction{
to_address_hash: to_address_hash,
from_address_hash: from_address_hash
}) do
[to_string(to_address_hash), to_string(from_address_hash)]
end
defp item_to_address_hash_strings(%Log{address_hash: address_hash}) do
[to_string(address_hash)]
end
defp item_to_address_hash_strings(%Withdrawal{address_hash: address_hash}) do
[to_string(address_hash)]
end
defp item_to_address_hash_strings(%Block{miner_hash: miner_hash}) do
[to_string(miner_hash)]
end
defp item_to_address_hash_strings(%CurrentTokenBalance{address_hash: address_hash}) do
[to_string(address_hash)]
end
defp item_to_address_hash_strings({%Address{} = address, _}) do
item_to_address_hash_strings(address)
end
defp item_to_address_hash_strings(%Address{hash: hash}) do
[to_string(hash)]
end
defp put_ens_names(names, items) do
Enum.map(items, &put_meta_to_item(&1, names, :ens_domain_name))
end
defp put_metadata(names, items) do
Enum.map(items, &put_meta_to_item(&1, names, :metadata))
end
defp put_meta_to_item(
%Transaction{
to_address_hash: to_address_hash,
created_contract_address_hash: created_contract_address_hash,
from_address_hash: from_address_hash
} = tx,
names,
field_to_put_info
) do
token_transfers =
case tx.token_transfers do
token_transfers_list when is_list(token_transfers_list) ->
Enum.map(token_transfers_list, &put_meta_to_item(&1, names, field_to_put_info))
other ->
other
end
%Transaction{
tx
| to_address: alter_address(tx.to_address, to_address_hash, names, field_to_put_info),
created_contract_address:
alter_address(tx.created_contract_address, created_contract_address_hash, names, field_to_put_info),
from_address: alter_address(tx.from_address, from_address_hash, names, field_to_put_info),
token_transfers: token_transfers
}
end
defp put_meta_to_item(
%TokenTransfer{
to_address_hash: to_address_hash,
from_address_hash: from_address_hash
} = tt,
names,
field_to_put_info
) do
%TokenTransfer{
tt
| to_address: alter_address(tt.to_address, to_address_hash, names, field_to_put_info),
from_address: alter_address(tt.from_address, from_address_hash, names, field_to_put_info)
}
end
defp put_meta_to_item(
%InternalTransaction{
to_address_hash: to_address_hash,
created_contract_address_hash: created_contract_address_hash,
from_address_hash: from_address_hash
} = tx,
names,
field_to_put_info
) do
%InternalTransaction{
tx
| to_address: alter_address(tx.to_address, to_address_hash, names, field_to_put_info),
created_contract_address:
alter_address(tx.created_contract_address, created_contract_address_hash, names, field_to_put_info),
from_address: alter_address(tx.from_address, from_address_hash, names, field_to_put_info)
}
end
defp put_meta_to_item(%Log{address_hash: address_hash} = log, names, field_to_put_info) do
%Log{log | address: alter_address(log.address, address_hash, names, field_to_put_info)}
end
defp put_meta_to_item(%Withdrawal{address_hash: address_hash} = withdrawal, names, field_to_put_info) do
%Withdrawal{withdrawal | address: alter_address(withdrawal.address, address_hash, names, field_to_put_info)}
end
defp put_meta_to_item(%Block{miner_hash: miner_hash} = block, names, field_to_put_info) do
%Block{block | miner: alter_address(block.miner, miner_hash, names, field_to_put_info)}
end
defp put_meta_to_item(
%CurrentTokenBalance{address_hash: address_hash} = current_token_balance,
names,
field_to_put_info
) do
%CurrentTokenBalance{
current_token_balance
| address: alter_address(current_token_balance.address, address_hash, names, field_to_put_info)
}
end
defp put_meta_to_item({%Address{} = address, count}, names, field_to_put_info) do
{put_meta_to_item(address, names, field_to_put_info), count}
end
defp put_meta_to_item(%Address{} = address, names, field_to_put_info) do
alter_address(address, address.hash, names, field_to_put_info)
end
defp alter_address(_, nil, _names, _field) do
nil
end
defp alter_address(%NotLoaded{}, address_hash, names, field) do
%{field => names[Address.checksum(address_hash)]}
end
defp alter_address(%Address{} = address, address_hash, names, :ens_domain_name) do
%Address{address | ens_domain_name: names[Address.checksum(address_hash)]}
end
defp alter_address(%Address{} = address, address_hash, names, :metadata) do
%Address{address | metadata: names[Address.checksum(address_hash)]}
end
end

@ -3,37 +3,21 @@ defmodule Explorer.MicroserviceInterfaces.BENS do
Interface to interact with Blockscout ENS microservice Interface to interact with Blockscout ENS microservice
""" """
alias Ecto.Association.NotLoaded
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Address.MetadataPreloader
alias Explorer.Chain.{ alias Explorer.Chain.{Address, Transaction}
Address,
Address.CurrentTokenBalance,
Block,
InternalTransaction,
Log,
TokenTransfer,
Transaction,
Withdrawal
}
alias Explorer.Utility.Microservice alias Explorer.Utility.Microservice
alias HTTPoison.Response alias HTTPoison.Response
require Logger require Logger
import Explorer.Chain.Address.MetadataPreloader, only: [maybe_preload_meta: 3]
@post_timeout :timer.seconds(5) @post_timeout :timer.seconds(5)
@request_error_msg "Error while sending request to BENS microservice" @request_error_msg "Error while sending request to BENS microservice"
@typep supported_types ::
Address.t()
| Block.t()
| CurrentTokenBalance.t()
| InternalTransaction.t()
| Log.t()
| TokenTransfer.t()
| Transaction.t()
| Withdrawal.t()
@doc """ @doc """
Batch request for ENS names via POST {{baseUrl}}/api/v1/:chainId/addresses:batch-resolve-names Batch request for ENS names via POST {{baseUrl}}/api/v1/:chainId/addresses:batch-resolve-names
""" """
@ -66,11 +50,17 @@ defmodule Explorer.MicroserviceInterfaces.BENS do
end end
end end
@spec get_address(binary()) :: {:error, :disabled | binary() | Jason.DecodeError.t()} | {:ok, any} @doc """
Request for ENS name via GET {{baseUrl}}/api/v1/:chainId/addresses/{address_hash}
"""
@spec get_address(binary()) :: map() | nil
def get_address(address) do def get_address(address) do
with :ok <- Microservice.check_enabled(__MODULE__) do result =
http_get_request(get_address_url(address), nil) with :ok <- Microservice.check_enabled(__MODULE__) do
end http_get_request(get_address_url(address), nil)
end
parse_get_address_response(result)
end end
@doc """ @doc """
@ -90,6 +80,15 @@ defmodule Explorer.MicroserviceInterfaces.BENS do
end end
end end
@doc """
Request for ENS name via GET {{baseUrl}}/api/v1/:chainId/domains:lookup
"""
@spec ens_domain_name_lookup(binary()) ::
nil | %{address_hash: binary(), expiry_date: any(), name: any(), names_count: integer()}
def ens_domain_name_lookup(domain) do
domain |> ens_domain_lookup() |> parse_lookup_response()
end
defp http_post_request(url, body) do defp http_post_request(url, body) do
headers = [{"Content-Type", "application/json"}] headers = [{"Content-Type", "application/json"}]
@ -134,8 +133,8 @@ defmodule Explorer.MicroserviceInterfaces.BENS do
end end
end end
@spec enabled?() :: boolean @spec enabled?() :: boolean()
def enabled?, do: Application.get_env(:explorer, __MODULE__)[:enabled] def enabled?, do: Microservice.check_enabled(__MODULE__) == :ok
defp batch_resolve_name_url do defp batch_resolve_name_url do
"#{addresses_url()}:batch-resolve-names" "#{addresses_url()}:batch-resolve-names"
@ -166,88 +165,6 @@ defmodule Explorer.MicroserviceInterfaces.BENS do
"#{Microservice.base_url(__MODULE__)}/api/v1/#{chain_id}" "#{Microservice.base_url(__MODULE__)}/api/v1/#{chain_id}"
end end
@doc """
Preload ENS info to list of entities if enabled?()
"""
@spec maybe_preload_ens([supported_types] | supported_types) :: [supported_types] | supported_types
def maybe_preload_ens(argument, function \\ &preload_ens_to_list/1) do
if enabled?() do
function.(argument)
else
argument
end
end
@spec maybe_preload_ens_info_to_search_results(list()) :: list()
def maybe_preload_ens_info_to_search_results(list) do
maybe_preload_ens(list, &preload_ens_info_to_search_results/1)
end
@spec maybe_preload_ens_to_transaction(Transaction.t()) :: Transaction.t()
def maybe_preload_ens_to_transaction(transaction) do
maybe_preload_ens(transaction, &preload_ens_to_transaction/1)
end
@spec preload_ens_to_transaction(Transaction.t()) :: Transaction.t()
def preload_ens_to_transaction(transaction) do
[transaction_with_ens] = preload_ens_to_list([transaction])
transaction_with_ens
end
@spec maybe_preload_ens_to_address(Address.t()) :: Address.t()
def maybe_preload_ens_to_address(address) do
maybe_preload_ens(address, &preload_ens_to_address/1)
end
@spec preload_ens_to_address(Address.t()) :: Address.t()
def preload_ens_to_address(address) do
[address_with_ens] = preload_ens_to_list([address])
address_with_ens
end
@doc """
Preload ENS names to list of entities
"""
@spec preload_ens_to_list([supported_types]) :: [supported_types]
def preload_ens_to_list(items) do
address_hash_strings =
Enum.reduce(items, [], fn item, acc ->
item_to_address_hash_strings(item) ++ acc
end)
case ens_names_batch_request(address_hash_strings) do
{:ok, result} ->
put_ens_names(result["names"], items)
_ ->
items
end
end
@doc """
Preload ENS info to search result, using get_address/1
"""
@spec preload_ens_info_to_search_results(list) :: list
def preload_ens_info_to_search_results(list) do
Enum.map(list, fn
%{type: "address", ens_info: ens_info} = search_result when not is_nil(ens_info) ->
search_result
%{type: "address"} = search_result ->
ens_info = search_result[:address_hash] |> get_address() |> parse_get_address_response()
Map.put(search_result, :ens_info, ens_info)
search_result ->
search_result
end)
end
@spec ens_domain_name_lookup(binary()) ::
nil | %{address_hash: binary(), expiry_date: any(), name: any(), names_count: integer()}
def ens_domain_name_lookup(domain) do
domain |> ens_domain_lookup() |> parse_lookup_response()
end
defp parse_lookup_response( defp parse_lookup_response(
{:ok, {:ok,
%{ %{
@ -293,160 +210,35 @@ defmodule Explorer.MicroserviceInterfaces.BENS do
defp parse_get_address_response(_), do: nil defp parse_get_address_response(_), do: nil
defp item_to_address_hash_strings(%Transaction{ @doc """
to_address_hash: to_address_hash, Preloads ENS data to the list if BENS is enabled
created_contract_address_hash: created_contract_address_hash, """
from_address_hash: from_address_hash, @spec maybe_preload_ens(MetadataPreloader.supported_input()) :: MetadataPreloader.supported_input()
token_transfers: token_transfers def maybe_preload_ens(argument) do
}) do maybe_preload_meta(argument, __MODULE__, &MetadataPreloader.preload_ens_to_list/1)
token_transfers_addresses =
case token_transfers do
token_transfers_list when is_list(token_transfers_list) ->
List.flatten(Enum.map(token_transfers_list, &item_to_address_hash_strings/1))
_ ->
[]
end
([to_address_hash, created_contract_address_hash, from_address_hash]
|> Enum.reject(&is_nil/1)
|> Enum.map(&to_string/1)) ++ token_transfers_addresses
end
defp item_to_address_hash_strings(%TokenTransfer{
to_address_hash: to_address_hash,
from_address_hash: from_address_hash
}) do
[to_string(to_address_hash), to_string(from_address_hash)]
end
defp item_to_address_hash_strings(%InternalTransaction{
to_address_hash: to_address_hash,
from_address_hash: from_address_hash
}) do
[to_string(to_address_hash), to_string(from_address_hash)]
end
defp item_to_address_hash_strings(%Log{address_hash: address_hash}) do
[to_string(address_hash)]
end
defp item_to_address_hash_strings(%Withdrawal{address_hash: address_hash}) do
[to_string(address_hash)]
end
defp item_to_address_hash_strings(%Block{miner_hash: miner_hash}) do
[to_string(miner_hash)]
end
defp item_to_address_hash_strings(%CurrentTokenBalance{address_hash: address_hash}) do
[to_string(address_hash)]
end
defp item_to_address_hash_strings({%Address{} = address, _}) do
item_to_address_hash_strings(address)
end
defp item_to_address_hash_strings(%Address{hash: hash}) do
[to_string(hash)]
end
defp put_ens_names(names, items) do
Enum.map(items, &put_ens_name_to_item(&1, names))
end
defp put_ens_name_to_item(
%Transaction{
to_address_hash: to_address_hash,
created_contract_address_hash: created_contract_address_hash,
from_address_hash: from_address_hash
} = tx,
names
) do
token_transfers =
case tx.token_transfers do
token_transfers_list when is_list(token_transfers_list) ->
Enum.map(token_transfers_list, &put_ens_name_to_item(&1, names))
other ->
other
end
%Transaction{
tx
| to_address: alter_address(tx.to_address, to_address_hash, names),
created_contract_address: alter_address(tx.created_contract_address, created_contract_address_hash, names),
from_address: alter_address(tx.from_address, from_address_hash, names),
token_transfers: token_transfers
}
end
defp put_ens_name_to_item(
%TokenTransfer{
to_address_hash: to_address_hash,
from_address_hash: from_address_hash
} = tt,
names
) do
%TokenTransfer{
tt
| to_address: alter_address(tt.to_address, to_address_hash, names),
from_address: alter_address(tt.from_address, from_address_hash, names)
}
end
defp put_ens_name_to_item(
%InternalTransaction{
to_address_hash: to_address_hash,
created_contract_address_hash: created_contract_address_hash,
from_address_hash: from_address_hash
} = tx,
names
) do
%InternalTransaction{
tx
| to_address: alter_address(tx.to_address, to_address_hash, names),
created_contract_address: alter_address(tx.created_contract_address, created_contract_address_hash, names),
from_address: alter_address(tx.from_address, from_address_hash, names)
}
end
defp put_ens_name_to_item(%Log{address_hash: address_hash} = log, names) do
%Log{log | address: alter_address(log.address, address_hash, names)}
end
defp put_ens_name_to_item(%Withdrawal{address_hash: address_hash} = withdrawal, names) do
%Withdrawal{withdrawal | address: alter_address(withdrawal.address, address_hash, names)}
end
defp put_ens_name_to_item(%Block{miner_hash: miner_hash} = block, names) do
%Block{block | miner: alter_address(block.miner, miner_hash, names)}
end
defp put_ens_name_to_item(%CurrentTokenBalance{address_hash: address_hash} = current_token_balance, names) do
%CurrentTokenBalance{
current_token_balance
| address: alter_address(current_token_balance.address, address_hash, names)
}
end
defp put_ens_name_to_item({%Address{} = address, count}, names) do
{put_ens_name_to_item(address, names), count}
end
defp put_ens_name_to_item(%Address{} = address, names) do
alter_address(address, address.hash, names)
end end
defp alter_address(_, nil, _names) do @doc """
nil Preloads ENS data to the list of the search results if BENS is enabled
"""
@spec maybe_preload_ens_info_to_search_results(list()) :: list()
def maybe_preload_ens_info_to_search_results(list) do
maybe_preload_meta(list, __MODULE__, &MetadataPreloader.preload_ens_info_to_search_results/1)
end end
defp alter_address(%NotLoaded{}, address_hash, names) do @doc """
%{ens_domain_name: names[to_string(address_hash)]} Preloads ENS data to the transaction results if BENS is enabled
"""
@spec maybe_preload_ens_to_transaction(Transaction.t()) :: Transaction.t()
def maybe_preload_ens_to_transaction(transaction) do
maybe_preload_meta(transaction, __MODULE__, &MetadataPreloader.preload_ens_to_transaction/1)
end end
defp alter_address(%Address{} = address, address_hash, names) do @doc """
%Address{address | ens_domain_name: names[to_string(address_hash)]} Preloads ENS data to the address results if BENS is enabled
"""
@spec maybe_preload_ens_to_address(Address.t()) :: Address.t()
def maybe_preload_ens_to_address(address) do
maybe_preload_meta(address, __MODULE__, &MetadataPreloader.preload_ens_to_address/1)
end end
end end

@ -0,0 +1,93 @@
defmodule Explorer.MicroserviceInterfaces.Metadata do
@moduledoc """
Module to interact with Metadata microservice
"""
alias Explorer.Chain.{Address.MetadataPreloader, Transaction}
alias Explorer.Utility.Microservice
alias HTTPoison.Response
import Explorer.Chain.Address.MetadataPreloader, only: [maybe_preload_meta: 3]
require Logger
@post_timeout :timer.seconds(5)
@tags_per_address_limit 5
@request_error_msg "Error while sending request to Metadata microservice"
@spec get_addresses_tags([String.t()]) :: {:error, :disabled | <<_::416>> | Jason.DecodeError.t()} | {:ok, any()}
def get_addresses_tags(addresses) do
with :ok <- Microservice.check_enabled(__MODULE__) do
body = %{
addresses: addresses,
tags: %{
limit: to_string(@tags_per_address_limit)
}
}
http_post_request(addresses_metadata_url(), body)
end
end
defp http_post_request(url, body) do
headers = [{"Content-Type", "application/json"}]
case HTTPoison.post(url, Jason.encode!(body), headers, recv_timeout: @post_timeout) do
{:ok, %Response{body: body, status_code: 200}} ->
body |> Jason.decode() |> decode_meta()
{_, error} ->
Logger.error(fn ->
[
"Error while sending request to Metadata microservice url: #{url}, body: #{inspect(body)}: ",
inspect(error)
]
end)
{:error, @request_error_msg}
end
end
defp addresses_metadata_url do
"#{base_url()}/metadata"
end
defp base_url do
"#{Microservice.base_url(__MODULE__)}/api/v1"
end
@spec enabled?() :: boolean()
def enabled?, do: Microservice.check_enabled(__MODULE__) == :ok
@doc """
Preloads metadata to supported entities if Metadata microservice is enabled
"""
@spec maybe_preload_metadata(MetadataPreloader.supported_input()) :: MetadataPreloader.supported_input()
def maybe_preload_metadata(argument) do
maybe_preload_meta(argument, __MODULE__, &MetadataPreloader.preload_metadata_to_list/1)
end
@doc """
Preloads metadata to transaction if Metadata microservice is enabled
"""
@spec maybe_preload_metadata_to_transaction(Transaction.t()) :: Transaction.t()
def maybe_preload_metadata_to_transaction(transaction) do
maybe_preload_meta(transaction, __MODULE__, &MetadataPreloader.preload_metadata_to_transaction/1)
end
defp decode_meta({:ok, %{"addresses" => addresses} = result}) do
prepared_address =
Enum.reduce(addresses, %{}, fn {address, meta}, acc ->
prepared_meta = Map.put(meta, "tags", meta["tags"] |> Enum.map(&decode_meta_in_tag/1))
Map.put(acc, address, prepared_meta)
end)
{:ok, Map.put(result, "addresses", prepared_address)}
end
defp decode_meta(other), do: other
defp decode_meta_in_tag(%{"meta" => meta} = tag) do
Map.put(tag, "meta", Jason.decode!(meta))
end
end

@ -473,6 +473,10 @@ config :explorer, Explorer.MicroserviceInterfaces.AccountAbstraction,
service_url: System.get_env("MICROSERVICE_ACCOUNT_ABSTRACTION_URL"), service_url: System.get_env("MICROSERVICE_ACCOUNT_ABSTRACTION_URL"),
enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED") enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED")
config :explorer, Explorer.MicroserviceInterfaces.Metadata,
service_url: System.get_env("MICROSERVICE_METADATA_URL"),
enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_METADATA_ENABLED")
config :explorer, :air_table_public_tags, config :explorer, :air_table_public_tags,
table_url: System.get_env("ACCOUNT_PUBLIC_TAGS_AIRTABLE_URL"), table_url: System.get_env("ACCOUNT_PUBLIC_TAGS_AIRTABLE_URL"),
api_key: System.get_env("ACCOUNT_PUBLIC_TAGS_AIRTABLE_API_KEY") api_key: System.get_env("ACCOUNT_PUBLIC_TAGS_AIRTABLE_API_KEY")

@ -318,6 +318,8 @@ MICROSERVICE_SIG_PROVIDER_URL=http://sig-provider:8050/
# MICROSERVICE_BENS_ENABLED= # MICROSERVICE_BENS_ENABLED=
#MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED=true #MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED=true
#MICROSERVICE_ACCOUNT_ABSTRACTION_URL= #MICROSERVICE_ACCOUNT_ABSTRACTION_URL=
# MICROSERVICE_METADATA_URL=
# MICROSERVICE_METADATA_ENABLED=
DECODE_NOT_A_CONTRACT_CALLS=true DECODE_NOT_A_CONTRACT_CALLS=true
# DATABASE_READ_ONLY_API_URL= # DATABASE_READ_ONLY_API_URL=
# ACCOUNT_DATABASE_URL= # ACCOUNT_DATABASE_URL=

Loading…
Cancel
Save