Noves.fi API proxy

pull/9056/head
Viktor Baranov 10 months ago
parent 48bfc78b81
commit 9825192aba
  1. 1
      CHANGELOG.md
  2. 8
      apps/block_scout_web/lib/block_scout_web/api_router.ex
  3. 8
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex
  4. 61
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/noves_fi_conroller.ex
  5. 7
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex
  6. 66
      apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex
  7. 5
      config/runtime.exs
  8. 2
      cspell.json
  9. 63
      docker-compose/envs/common-blockscout.env

@ -5,6 +5,7 @@
### Features
- [#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
### Fixes

@ -300,6 +300,14 @@ defmodule BlockScoutWeb.ApiRouter do
get("/batches/:batch_number", V2.ZkevmController, :batch)
end
end
scope "/proxy" do
scope "/noves-fi" do
get("/transactions/:transaction_hash_param", V2.Proxy.NovesFiController, :transaction)
get("/transactions/:transaction_hash_param/describe", V2.Proxy.NovesFiController, :describe_transaction)
get("/addresses/:address_hash_param/transactions", V2.Proxy.NovesFiController, :address_transactions)
end
end
end
scope "/v1", as: :api_v1 do

@ -25,7 +25,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
alias BlockScoutWeb.AccessHelper
alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView}
alias Explorer.{Chain, Market}
alias Explorer.Chain.{Address, Transaction}
alias Explorer.Chain.{Address, Hash, Transaction}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.Token.Instance
alias Indexer.Fetcher.{CoinBalanceOnDemand, TokenBalanceOnDemand}
@ -497,7 +497,11 @@ defmodule BlockScoutWeb.API.V2.AddressController do
end
end
defp validate_address(address_hash_string, params, options \\ @api_true) do
@doc """
Checks if this valid address hash string, and this address is not prohibited address
"""
@spec validate_address(String.t(), any(), list()) :: {:ok, Hash.Address.t(), Address.t()}
def validate_address(address_hash_string, params, options \\ @api_true) do
with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)},
{:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params),
{:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash, options, false)} do

@ -0,0 +1,61 @@
defmodule BlockScoutWeb.API.V2.Proxy.NovesFiController do
use BlockScoutWeb, :controller
alias BlockScoutWeb.API.V2.{AddressController, TransactionController}
alias Explorer.ThirdPartyIntegrations.NovesFi
action_fallback(BlockScoutWeb.API.V2.FallbackController)
@doc """
Function to handle GET requests to `/api/v2/proxy/noves-fi/transactions/:transaction_hash_param` endpoint.
"""
@spec transaction(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()}
def transaction(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do
with {:ok, _transaction, _transaction_hash} <-
TransactionController.validate_transaction(transaction_hash_string, params,
necessity_by_association: %{},
api?: true
),
url = NovesFi.tx_url(transaction_hash_string),
{response, status} <- NovesFi.noves_fi_api_request(url, conn),
{:is_empty_response, false} <- {:is_empty_response, is_nil(response)} do
conn
|> put_status(status)
|> json(response)
end
end
@doc """
Function to handle GET requests to `/api/v2/proxy/noves-fi/transactions/:transaction_hash_param/describe` endpoint.
"""
@spec describe_transaction(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()}
def describe_transaction(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do
with {:ok, _transaction, _transaction_hash} <-
TransactionController.validate_transaction(transaction_hash_string, params,
necessity_by_association: %{},
api?: true
),
url = NovesFi.describe_tx_url(transaction_hash_string),
{response, status} <- NovesFi.noves_fi_api_request(url, conn),
{:is_empty_response, false} <- {:is_empty_response, is_nil(response)} do
conn
|> put_status(status)
|> json(response)
end
end
@doc """
Function to handle GET requests to `/api/v2/proxy/noves-fi/transactions/:transaction_hash_param/describe` endpoint.
"""
@spec address_transactions(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()}
def address_transactions(conn, %{"address_hash_param" => address_hash_string} = params) do
with {:ok, _address_hash, _address} <- AddressController.validate_address(address_hash_string, params),
url = NovesFi.address_txs_url(address_hash_string),
{response, status} <- NovesFi.noves_fi_api_request(url, conn),
{:is_empty_response, false} <- {:is_empty_response, is_nil(response)} do
conn
|> put_status(status)
|> json(response)
end
end
end

@ -28,6 +28,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
alias BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation, as: TransactionInterpretationService
alias BlockScoutWeb.Models.TransactionStateHelper
alias Explorer.Chain
alias Explorer.Chain.{Hash, Transaction}
alias Explorer.Chain.Zkevm.Reader
alias Indexer.Fetcher.FirstTraceOnDemand
@ -388,7 +389,11 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
end
end
defp validate_transaction(transaction_hash_string, params, options \\ @api_true) do
@doc """
Checks if this valid transaction hash string, and this transaction doesn't belong to prohibited address
"""
@spec validate_transaction(String.t(), any(), list()) :: {:ok, Transaction.t(), Hash.t()}
def validate_transaction(transaction_hash_string, params, options \\ @api_true) do
with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
{:not_found, {:ok, transaction}} <-
{:not_found, Chain.hash_to_transaction(transaction_hash, options)},

@ -0,0 +1,66 @@
defmodule Explorer.ThirdPartyIntegrations.NovesFi do
@moduledoc """
Module for Noves.Fi API integration https://blockscout.noves.fi/swagger/index.html
"""
alias Explorer.Helper
@recv_timeout 60_000
@doc """
Proxy request to noves.fi API endpoints
"""
@spec noves_fi_api_request(String.t(), Plug.Conn.t()) :: any()
def noves_fi_api_request(url, conn) do
headers = [{"apiKey", api_key()}]
url_with_params = url <> "?" <> conn.query_string
case HTTPoison.get(url_with_params, headers, recv_timeout: @recv_timeout) do
{:ok, %HTTPoison.Response{status_code: status, body: body}} ->
{Helper.decode_json(body), status}
_ ->
nil
end
end
@doc """
Noves.fi /evm/{chain}/tx/{txHash} endpoint
"""
@spec tx_url(String.t()) :: String.t()
def tx_url(transaction_hash_string) do
"#{base_url()}/evm/#{chain_name()}/tx/#{transaction_hash_string}"
end
@doc """
Noves.fi /evm/{chain}/describeTx/{txHash} endpoint
"""
@spec describe_tx_url(String.t()) :: String.t()
def describe_tx_url(transaction_hash_string) do
"#{base_url()}/evm/#{chain_name()}/describeTx/#{transaction_hash_string}"
end
@doc """
Noves.fi /evm/{chain}/txs/{accountAddress} endpoint
"""
@spec address_txs_url(String.t()) :: String.t()
def address_txs_url(address_hash_string) do
"#{base_url()}/evm/#{chain_name()}/txs/#{address_hash_string}"
end
defp base_url do
api_base_url() || "https://blockscout.noves.fi"
end
defp api_base_url do
Application.get_env(:explorer, __MODULE__)[:api_base_url]
end
defp chain_name do
Application.get_env(:explorer, __MODULE__)[:chain_name]
end
defp api_key do
Application.get_env(:explorer, __MODULE__)[:api_key]
end
end

@ -378,6 +378,11 @@ config :explorer, Explorer.ThirdPartyIntegrations.SolidityScan,
chain_id: System.get_env("SOLIDITYSCAN_CHAIN_ID"),
api_key: System.get_env("SOLIDITYSCAN_API_TOKEN")
config :explorer, Explorer.ThirdPartyIntegrations.NovesFi,
api_base_url: System.get_env("NOVES_FI_BASE_API_URL"),
chain_name: System.get_env("NOVES_FI_CHAIN_NAME"),
api_key: System.get_env("NOVES_FI_API_TOKEN")
enabled? = ConfigHelper.parse_bool_env_var("MICROSERVICE_SC_VERIFIER_ENABLED")
# or "eth_bytecode_db"
type = System.get_env("MICROSERVICE_SC_VERIFIER_TYPE", "sc_verifier")

@ -312,6 +312,8 @@
"noproc",
"noreferrer",
"noreply",
"noves",
"NovesFi",
"nowarn",
"nowrap",
"ntoa",

@ -99,8 +99,20 @@ CONTRACT_MAX_STRING_LENGTH_WITHOUT_TRIMMING=2040
# CONTRACT_DISABLE_INTERACTION=
UNCLES_IN_AVERAGE_BLOCK_TIME=false
DISABLE_WEBAPP=false
API_V2_ENABLED=true
API_V1_READ_METHODS_DISABLED=false
API_V1_WRITE_METHODS_DISABLED=false
#API_RATE_LIMIT_DISABLED=true
# API_SENSITIVE_ENDPOINTS_KEY=
API_RATE_LIMIT_TIME_INTERVAL=1s
API_RATE_LIMIT_BY_IP_TIME_INTERVAL=5m
API_RATE_LIMIT=50
API_RATE_LIMIT_BY_KEY=50
API_RATE_LIMIT_BY_WHITELISTED_IP=50
API_RATE_LIMIT_WHITELISTED_IPS=
API_RATE_LIMIT_STATIC_API_KEY=
API_RATE_LIMIT_UI_V2_WITH_TOKEN=5
API_RATE_LIMIT_BY_IP=3000
DISABLE_INDEXER=false
DISABLE_REALTIME_INDEXER=false
DISABLE_CATCHUP_INDEXER=false
@ -119,10 +131,16 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false
# INDEXER_BLOCK_REWARD_BATCH_SIZE=
# INDEXER_BLOCK_REWARD_CONCURRENCY=
# INDEXER_TOKEN_INSTANCE_RETRY_REFETCH_INTERVAL=
# INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE=10
# INDEXER_TOKEN_INSTANCE_RETRY_CONCURRENCY=
# INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE=1
# INDEXER_TOKEN_INSTANCE_REALTIME_CONCURRENCY=
# INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE=10
# INDEXER_TOKEN_INSTANCE_SANITIZE_CONCURRENCY=
# INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_BATCH_SIZE=10
# INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_CONCURRENCY=10
# TOKEN_INSTANCE_OWNER_MIGRATION_CONCURRENCY=5
# TOKEN_INSTANCE_OWNER_MIGRATION_BATCH_SIZE=50
# INDEXER_COIN_BALANCES_BATCH_SIZE=
# INDEXER_COIN_BALANCES_CONCURRENCY=
# INDEXER_RECEIPTS_BATCH_SIZE=
@ -194,31 +212,16 @@ EXTERNAL_APPS=[]
# RESTRICTED_LIST_KEY=
SHOW_MAINTENANCE_ALERT=false
MAINTENANCE_ALERT_MESSAGE=
SOURCIFY_INTEGRATION_ENABLED=false
SOURCIFY_SERVER_URL=
SOURCIFY_REPO_URL=
CHAIN_ID=
MAX_SIZE_UNLESS_HIDE_ARRAY=50
HIDE_BLOCK_MINER=false
DISPLAY_TOKEN_ICONS=false
SHOW_TENDERLY_LINK=false
TENDERLY_CHAIN_PATH=
RE_CAPTCHA_SECRET_KEY=
RE_CAPTCHA_CLIENT_KEY=
RE_CAPTCHA_V3_SECRET_KEY=
RE_CAPTCHA_V3_CLIENT_KEY=
RE_CAPTCHA_DISABLED=false
JSON_RPC=
#API_RATE_LIMIT_DISABLED=true
API_RATE_LIMIT_TIME_INTERVAL=1s
API_RATE_LIMIT_BY_IP_TIME_INTERVAL=5m
API_RATE_LIMIT=50
API_RATE_LIMIT_BY_KEY=50
API_RATE_LIMIT_BY_WHITELISTED_IP=50
API_RATE_LIMIT_WHITELISTED_IPS=
API_RATE_LIMIT_STATIC_API_KEY=
API_RATE_LIMIT_UI_V2_WITH_TOKEN=5
API_RATE_LIMIT_BY_IP=3000
# API_RATE_LIMIT_HAMMER_REDIS_URL=redis://redis_db:6379/1
# API_RATE_LIMIT_IS_BLOCKSCOUT_BEHIND_PROXY=false
API_RATE_LIMIT_UI_V2_TOKEN_TTL_IN_SECONDS=18000
@ -234,6 +237,8 @@ MICROSERVICE_VISUALIZE_SOL2UML_ENABLED=true
MICROSERVICE_VISUALIZE_SOL2UML_URL=http://visualizer:8050/
MICROSERVICE_SIG_PROVIDER_ENABLED=true
MICROSERVICE_SIG_PROVIDER_URL=http://sig-provider:8050/
# MICROSERVICE_BENS_URL=
# MICROSERVICE_BENS_ENABLED=
DECODE_NOT_A_CONTRACT_CALLS=true
# DATABASE_READ_ONLY_API_URL=
# ACCOUNT_DATABASE_URL=
@ -246,28 +251,28 @@ DECODE_NOT_A_CONTRACT_CALLS=true
# ACCOUNT_SENDGRID_API_KEY=
# ACCOUNT_SENDGRID_SENDER=
# ACCOUNT_SENDGRID_TEMPLATE=
# ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL=
# ACCOUNT_PRIVATE_TAGS_LIMIT=2000
# ACCOUNT_WATCHLIST_ADDRESSES_LIMIT=15
ACCOUNT_CLOAK_KEY=
ACCOUNT_ENABLED=false
ACCOUNT_REDIS_URL=redis://redis_db:6379
EIP_1559_ELASTICITY_MULTIPLIER=2
# MIXPANEL_TOKEN=
# MIXPANEL_URL=
# AMPLITUDE_API_KEY=
# AMPLITUDE_URL=
EIP_1559_ELASTICITY_MULTIPLIER=2
# API_SENSITIVE_ENDPOINTS_KEY=
# ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL=
# INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE=10
# INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE=1
# INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE=10
# INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_BATCH_SIZE=10
# TOKEN_INSTANCE_OWNER_MIGRATION_CONCURRENCY=5
# TOKEN_INSTANCE_OWNER_MIGRATION_BATCH_SIZE=50
# IPFS_GATEWAY_URL=
API_V2_ENABLED=true
# ADDRESSES_TABS_COUNTERS_TTL=10m
# ACCOUNT_PRIVATE_TAGS_LIMIT=2000
# ACCOUNT_WATCHLIST_ADDRESSES_LIMIT=15
# MICROSERVICE_BENS_URL=
# MICROSERVICE_BENS_ENABLED=
# DENORMALIZATION_MIGRATION_BATCH_SIZE=
# DENORMALIZATION_MIGRATION_CONCURRENCY=
SOURCIFY_INTEGRATION_ENABLED=false
SOURCIFY_SERVER_URL=
SOURCIFY_REPO_URL=
SHOW_TENDERLY_LINK=false
TENDERLY_CHAIN_PATH=
# SOLIDITYSCAN_CHAIN_ID=
# SOLIDITYSCAN_API_TOKEN=
# NOVES_FI_BASE_API_URL=
# NOVES_FI_CHAIN_NAME=
# NOVES_FI_API_TOKEN=

Loading…
Cancel
Save