Add visualuze sol2uml controller

pull/6401/head
Lymarenko Lev 2 years ago
parent f15ca8eeee
commit 9bcdea8204
  1. 41
      apps/block_scout_web/lib/block_scout_web/controllers/visualize_sol2uml_controller.ex
  2. 22
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  3. 16
      apps/block_scout_web/lib/block_scout_web/templates/visualize_sol2uml/index.html.eex
  4. 3
      apps/block_scout_web/lib/block_scout_web/views/visualize_sol2uml_view.ex
  5. 2
      apps/block_scout_web/lib/block_scout_web/web_router.ex
  6. 74
      apps/explorer/lib/explorer/visualize/sol2uml.ex
  7. 4
      config/runtime.exs

@ -0,0 +1,41 @@
defmodule BlockScoutWeb.VisualizeSol2umlController do
use BlockScoutWeb, :controller
alias Explorer.Chain
alias Explorer.Visualize.Sol2uml
def index(conn, %{"address" => address_hash_string}) do
address_options = [
necessity_by_association: %{
:contracts_creation_internal_transaction => :optional,
:names => :optional,
:smart_contract => :optional,
:token => :optional,
:contracts_creation_transaction => :optional
}
]
with true <- Sol2uml.enabled?(),
true <- Chain.smart_contract_fully_verified?(address_hash_string),
{:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do
sources = address.smart_contract_additional_sources
|> Enum.map(fn (additional_source) -> {additional_source.file_name, additional_source.contract_source_code} end)
|> Enum.into(%{})
|> Map.merge(%{
address.smart_contract.file_path => address.smart_contract.contract_source_code
})
params = %{
sources: sources
}
case Sol2uml.visualize_contracts(params) do
{:ok, svg} -> render(conn, "index.html", address: address, svg: svg, error: nil)
{:error, error} -> render(conn, "index.html", address: address, svg: nil, error: error)
end
else
_ -> not_found(conn)
end
end
def index(conn, params) do
not_found(conn)
end
end

@ -5,6 +5,7 @@
<% additional_sources_from_twin = Chain.get_address_verified_twin_contract(@address.hash).additional_sources %>
<% fully_verified = Chain.smart_contract_fully_verified?(@address.hash)%>
<% additional_sources = if smart_contract_verified, do: @address.smart_contract_additional_sources, else: additional_sources_from_twin %>
<% visualize_sol2uml_enabled = Explorer.Visualize.Sol2uml.enabled?() %>
<section class="container">
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>
@ -23,7 +24,7 @@
<div style="display: inline-block;">
<%= render BlockScoutWeb.CommonComponentsView, "_info.html" %>
<span> <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link(
metadata_for_verification.address_hash,
metadata_for_verification.address_hash,
to: address_contract_path(@conn, :index, metadata_for_verification.address_hash)) %>.<br/> <%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <i><%= gettext("Verify & Publish") %></i> <%= gettext("button") %></span>
</div>
<%= link(gettext("Verify & Publish"), to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish") %>
@ -98,9 +99,20 @@
<section>
<div class="d-flex justify-content-between align-items-baseline">
<h3><%= target_contract.file_path || gettext "Contract source code" %></h3>
<button type="button" class="btn-line" id="button" data-toggle="tooltip" data-placement="top" data-clipboard-text="<%= target_contract.contract_source_code %>" aria-label="Copy Contract Source Code">
<%= gettext "Copy Source Code" %>
</button>
<div class="d-flex justify-content-end">
<%= if visualize_sol2uml_enabled do %>
<a href="<%= visualize_sol2uml_path(@conn, :index, address: @address) %>" style="text-decoration: none;">
<div class="btn-line">
<div class="badge badge-info mr-1">new</div>
<i class="fa-solid fa-diagram-project mr-1"></i>
<span>Sol2uml</span>
</div>
</a>
<% end %>
<button type="button" class="btn-line ml-1" id="button" data-toggle="tooltip" data-placement="top" data-clipboard-text="<%= target_contract.contract_source_code %>" aria-label="Copy Contract Source Code">
<%= gettext "Copy Source Code" %>
</button>
</div>
</div>
<pre class="tile-muted tile-code mb-4" id="code_viewer_main" data-additional-sources-length=<%= Enum.count(additional_sources) %>><%= target_contract.contract_source_code %><pre>
</section>
@ -171,7 +183,7 @@
<div class="tile tile-muted mb-4">
<pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= creation_code(@address) %></code></pre>
</div>
<% end %>
<% end %>
<%= if fully_verified do %>
<div class="d-flex justify-content-between align-items-baseline">
<h3><%= gettext "Deployed ByteCode" %></h3>

@ -0,0 +1,16 @@
<section class="container">
<div class="card">
<div class="card-body fs-14">
<div class="card-title lg-card-title mb-2">
<h1><%= gettext("UML diagram") %></h1>
<%= gettext("For contract") %> <%= link(
@address.hash,
to: address_contract_path(@conn, :index, @address.hash)
) %>
</div>
<div class="row d-flex justify-content-center">
<img src="data:image/svg+xml;base64,<%= @svg %>"/>
</div>
</div>
</div>
</section>

@ -0,0 +1,3 @@
defmodule BlockScoutWeb.VisualizeSol2umlView do
use BlockScoutWeb, :view
end

@ -494,6 +494,8 @@ defmodule BlockScoutWeb.WebRouter do
get("/token-counters", Tokens.TokenController, :token_counters)
get("/vis/sol2uml", VisualizeSol2umlController, :index)
get("/*path", PageNotFoundController, :index)
end
end

@ -0,0 +1,74 @@
defmodule Explorer.Visualize.Sol2uml do
@moduledoc """
Adapter for sol2uml visualizer with https://github.com/blockscout/blockscout-rs/blob/main/visualizer
"""
alias HTTPoison.Response
require Logger
@post_timeout :infinity
@request_error_msg "Error while sending request to visualizer microservice"
def visualize_contracts(body) do
http_post_request(visualize_contracts_url(), body)
end
def 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}} ->
proccess_visualizer_response(body)
{:ok, %Response{body: body, status_code: _}} ->
proccess_visualizer_response(body)
{:error, error} ->
old_truncate = Application.get_env(:logger, :truncate)
Logger.configure(truncate: :infinity)
Logger.error(fn ->
[
"Error while sending request to visualizer microservice. url: #{url}, body: #{inspect(body, limit: :infinity, printable_limit: :infinity)}: ",
inspect(error, limit: :infinity, printable_limit: :infinity)
]
end)
Logger.configure(truncate: old_truncate)
{:error, @request_error_msg}
end
end
def proccess_visualizer_response(body) when is_binary(body) do
case Jason.decode(body) do
{:ok, decoded} ->
proccess_visualizer_response(decoded)
_ ->
{:error, body}
end
end
def proccess_visualizer_response(%{"svg" => svg}) do
{:ok, svg}
end
def proccess_visualizer_response(other), do: {:error, other}
def visualize_contracts_url, do: "#{base_api_url()}" <> "/solidity:visualizeContracts"
def base_api_url, do: "#{base_url()}" <> "/api/v1"
def base_url do
url = Application.get_env(:explorer, __MODULE__)[:service_url]
if String.ends_with?(url, "/") do
url
|> String.slice(0..(String.length(url) - 2))
else
url
end
end
def enabled?, do: Application.get_env(:explorer, __MODULE__)[:enabled]
end

@ -215,6 +215,10 @@ config :explorer,
else: Explorer.Chain.Events.DBSender
)
config :explorer, Explorer.Visualize.Sol2uml,
service_url: System.get_env("VISUALIZE_SOL2UML_SERVICE_URL"),
enabled: System.get_env("VISUALIZE_SOL2UML_ENABLED") == "true"
config :explorer, Explorer.Chain.Events.Listener,
enabled:
if(disable_webapp == "true" && disable_indexer == "true",

Loading…
Cancel
Save