From 52845654df6449589e00178dfb281defc980aea9 Mon Sep 17 00:00:00 2001 From: Amanda Sposito Date: Wed, 15 Aug 2018 14:18:52 -0300 Subject: [PATCH] Refactor 'Read Smart Contract' functionality to be via AJAX --- apps/block_scout_web/assets/js/app.js | 2 +- ...ead_function.js => read_only_functions.js} | 26 ++++- .../address_read_contract_controller.ex | 33 ------- .../controllers/smart_contract_controller.ex | 67 +++++++++++++ .../address_read_contract/index.html.eex | 58 +---------- .../_function_response.html.eex | 8 ++ .../smart_contract/_functions.html.eex | 50 ++++++++++ .../views/smart_contract_view.ex | 7 ++ apps/block_scout_web/priv/gettext/default.pot | 49 +++++----- .../priv/gettext/en/LC_MESSAGES/default.po | 49 +++++----- .../address_read_contract_controller_test.exs | 55 +++++++---- .../smart_contract_controller_test.exs | 97 +++++++++++++++++++ .../tokens/read_contract_controller_test.exs | 37 +++++++ apps/block_scout_web/test/test_helper.exs | 2 + 14 files changed, 384 insertions(+), 156 deletions(-) rename apps/block_scout_web/assets/js/lib/smart_contract/{read_function.js => read_only_functions.js} (57%) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs diff --git a/apps/block_scout_web/assets/js/app.js b/apps/block_scout_web/assets/js/app.js index a48a4dbde7..d70012eff8 100644 --- a/apps/block_scout_web/assets/js/app.js +++ b/apps/block_scout_web/assets/js/app.js @@ -24,7 +24,7 @@ import './lib/loading_element' import './lib/market_history_chart' import './lib/reload_button' import './lib/tooltip' -import './lib/smart_contract/read_function' +import './lib/smart_contract/read_only_functions' import './lib/pretty_json' import './lib/try_api' diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/read_function.js b/apps/block_scout_web/assets/js/lib/smart_contract/read_only_functions.js similarity index 57% rename from apps/block_scout_web/assets/js/lib/smart_contract/read_function.js rename to apps/block_scout_web/assets/js/lib/smart_contract/read_only_functions.js index a80e05cae9..9c7e3240b8 100644 --- a/apps/block_scout_web/assets/js/lib/smart_contract/read_function.js +++ b/apps/block_scout_web/assets/js/lib/smart_contract/read_only_functions.js @@ -1,5 +1,25 @@ import $ from 'jquery' +const loadFunctions = (element) => { + const $element = $(element) + const url = $element.data('url') + const hash = $element.data('hash') + + $.get( + url, + { hash: hash }, + response => $element.html(response) + ) + .done(function () { + $('[data-function]').each((_, element) => { + readFunction(element) + }) + }) + .fail(function (response) { + $element.html(response.statusText) + }) +} + const readFunction = (element) => { const $element = $(element) const $form = $element.find('[data-function-form]') @@ -26,6 +46,6 @@ const readFunction = (element) => { }) } -$('[data-function]').each((_, element) => { - readFunction(element) -}) +const container = $('[data-smart-contract-functions]') + +loadFunctions(container) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex index 28b4ada396..4eb8bed8b9 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex @@ -3,19 +3,15 @@ defmodule BlockScoutWeb.AddressReadContractController do alias Explorer.{Chain, Market} alias Explorer.ExchangeRates.Token - alias Explorer.SmartContract.Reader import BlockScoutWeb.AddressController, only: [transaction_count: 1] def index(conn, %{"address_id" => address_hash_string}) do with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:ok, address} <- Chain.find_contract_address(address_hash) do - read_only_functions = Reader.read_only_functions(address_hash) - render( conn, "index.html", - read_only_functions: read_only_functions, address: address, exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), transaction_count: transaction_count(address) @@ -28,33 +24,4 @@ defmodule BlockScoutWeb.AddressReadContractController do not_found(conn) end end - - def show(conn, params) do - with true <- ajax?(conn), - {:ok, address_hash} <- Chain.string_to_address_hash(params["address_id"]), - outputs = - Reader.query_function( - address_hash, - %{name: params["function_name"], args: params["args"]} - ) do - conn - |> put_status(200) - |> put_layout(false) - |> render( - "_function_response.html", - function_name: params["function_name"], - outputs: outputs - ) - else - _ -> - not_found(conn) - end - end - - defp ajax?(conn) do - case get_req_header(conn, "x-requested-with") do - [value] -> value in ["XMLHttpRequest", "xmlhttprequest"] - [] -> false - end - end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex new file mode 100644 index 0000000000..271068f0c8 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex @@ -0,0 +1,67 @@ +defmodule BlockScoutWeb.SmartContractController do + use BlockScoutWeb, :controller + + alias Explorer.Chain + alias Explorer.SmartContract.Reader + + def index(conn, %{"hash" => address_hash_string}) do + with true <- ajax?(conn), + {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash) do + read_only_functions = Reader.read_only_functions(address_hash) + + conn + |> put_status(200) + |> put_layout(false) + |> render( + "_functions.html", + read_only_functions: read_only_functions, + address: address + ) + else + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + + _ -> + not_found(conn) + end + end + + def show(conn, params) do + with true <- ajax?(conn), + {:ok, address_hash} <- Chain.string_to_address_hash(params["id"]), + outputs = + Reader.query_function( + address_hash, + %{name: params["function_name"], args: params["args"]} + ) do + conn + |> put_status(200) + |> put_layout(false) + |> render( + "_function_response.html", + function_name: params["function_name"], + outputs: outputs + ) + else + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + + _ -> + not_found(conn) + end + end + + defp ajax?(conn) do + case get_req_header(conn, "x-requested-with") do + [value] -> value in ["XMLHttpRequest", "xmlhttprequest"] + [] -> false + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex index 613d94913b..670f1d5470 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex @@ -40,61 +40,9 @@ -
-

<%= gettext("Read Contract Information") %>

- - <%= for {function, counter} <- Enum.with_index(@read_only_functions, 1) do %> -
-
- <%= counter %>. - - <%= function["name"] %> - - → -
- - <%= if queryable?(function["inputs"]) do %> -
-
- - - <%= for input <- function["inputs"] do %> -
- -
- <% end %> - - -
- -
- <%= if (queryable?(function["inputs"])), do: raw "↳" %> - - <%= for output <- function["outputs"] do %> - <%= output["type"] %> - <% end %> -
- -
-
- - <% else %> - - <%= for output <- function["outputs"] do %> - <%= if address?(output["type"]) do %> - <%= link( - output["value"], - to: address_path(@conn, :show, @conn.assigns.locale, output["value"]) - ) %> - <% else %> - <%= output["value"] %> - <% end %> - <% end %> - - <% end %> - -
- <% end %> + +
+ <%= gettext("loading...") %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex new file mode 100644 index 0000000000..5ba822b1d6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex @@ -0,0 +1,8 @@ +
+ [ <%= @function_name %> method Response ] + + <%= for item <- @outputs do %> + + <%= item["type"] %> : <%= item["value"] %> + <% end %> +
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex new file mode 100644 index 0000000000..419aa45ad4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex @@ -0,0 +1,50 @@ +<%= for {function, counter} <- Enum.with_index(@read_only_functions, 1) do %> +
+
+ <%= counter %>. + + <%= function["name"] %> + + → +
+ + <%= if queryable?(function["inputs"]) do %> +
+
+ + + <%= for input <- function["inputs"] do %> +
+ +
+ <% end %> + + +
+ +
+ <%= if (queryable?(function["inputs"])), do: raw "↳" %> + + <%= for output <- function["outputs"] do %> + <%= output["type"] %> + <% end %> +
+ +
+
+ <% else %> + + <%= for output <- function["outputs"] do %> + <%= if address?(output["type"]) do %> + <%= link( + output["value"], + to: address_path(@conn, :show, @conn.assigns.locale, output["value"]) + ) %> + <% else %> + <%= output["value"] %> + <% end %> + <% end %> + + <% end %> +
+<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex new file mode 100644 index 0000000000..b967f78882 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex @@ -0,0 +1,7 @@ +defmodule BlockScoutWeb.SmartContractView do + use BlockScoutWeb, :view + + def queryable?(inputs), do: Enum.any?(inputs) + + def address?(type), do: type == "address" +end diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index ecdf2590fa..719cc92aff 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -481,7 +481,7 @@ msgstr "" #: lib/block_scout_web/templates/block/index.html.eex:15 #: lib/block_scout_web/templates/block_transaction/index.html.eex:51 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:78 -#: lib/block_scout_web/templates/token/show.html.eex:102 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:71 #: lib/block_scout_web/templates/transaction/index.html.eex:66 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:72 msgid "Older" @@ -637,7 +637,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_transaction/_transaction.html.eex:41 -#: lib/block_scout_web/templates/token/_token_transfer.html.eex:38 +#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:38 #: lib/block_scout_web/templates/transaction/_tile.html.eex:28 msgid "Block #%{number}" msgstr "" @@ -659,7 +659,7 @@ msgid "OUT" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_read_contract/index.html.eex:67 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:22 msgid "Query" msgstr "" @@ -668,14 +668,13 @@ msgstr "" #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:42 #: lib/block_scout_web/templates/address_read_contract/index.html.eex:36 #: lib/block_scout_web/templates/address_transaction/index.html.eex:42 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:42 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:26 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:45 msgid "Read Contract" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/address_read_contract/index.html.eex:44 -msgid "Read Contract Information" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/block/overview.html.eex:12 msgid "%{count} Transactions" @@ -723,7 +722,7 @@ msgid "Used" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/_token_transfer.html.eex:4 +#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4 #: lib/block_scout_web/views/transaction_view.ex:116 msgid "Token Transfer" @@ -731,7 +730,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_transaction/_transaction.html.eex:64 -#: lib/block_scout_web/templates/token/show.html.eex:20 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:18 msgid "Transfers" msgstr "" @@ -759,20 +758,23 @@ msgstr "" msgid "Validated Transactions" msgstr "" -#: lib/block_scout_web/templates/token/show.html.eex:95 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:64 msgid "There are no transfers for this Token." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:11 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:10 msgid "Token Details" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:63 -#: lib/block_scout_web/templates/token/show.html.eex:73 -#: lib/block_scout_web/templates/token/show.html.eex:76 -#: lib/block_scout_web/templates/token/show.html.eex:86 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:17 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:34 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:37 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:17 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:36 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:39 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:55 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:12 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:42 #: lib/block_scout_web/templates/transaction_log/index.html.eex:13 @@ -785,17 +787,17 @@ msgid "Token Transfers" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:19 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:17 msgid "addresses" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:22 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:20 msgid "decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:33 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:31 msgid "Total Supply" msgstr "" @@ -805,11 +807,12 @@ msgid "API" msgstr "" #, elixir-format -#: lib/block_scout_web/views/token_helpers.ex:38 -msgid "ERC-721 TokenID [%{token_id}]" +#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:65 +msgid "There are no token transfers for this transaction." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:65 -msgid "There are no token transfers for this transaction." +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:45 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:52 +msgid "loading..." msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index 565e777856..cb18524956 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -493,7 +493,7 @@ msgstr "" #: lib/block_scout_web/templates/block/index.html.eex:15 #: lib/block_scout_web/templates/block_transaction/index.html.eex:51 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:78 -#: lib/block_scout_web/templates/token/show.html.eex:102 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:71 #: lib/block_scout_web/templates/transaction/index.html.eex:66 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:72 msgid "Older" @@ -649,7 +649,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_transaction/_transaction.html.eex:41 -#: lib/block_scout_web/templates/token/_token_transfer.html.eex:38 +#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:38 #: lib/block_scout_web/templates/transaction/_tile.html.eex:28 msgid "Block #%{number}" msgstr "" @@ -671,7 +671,7 @@ msgid "OUT" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_read_contract/index.html.eex:67 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:22 msgid "Query" msgstr "" @@ -680,14 +680,13 @@ msgstr "" #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:42 #: lib/block_scout_web/templates/address_read_contract/index.html.eex:36 #: lib/block_scout_web/templates/address_transaction/index.html.eex:42 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:25 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:42 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:26 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:45 msgid "Read Contract" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/address_read_contract/index.html.eex:44 -msgid "Read Contract Information" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/block/overview.html.eex:12 msgid "%{count} Transactions" @@ -735,7 +734,7 @@ msgid "Used" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/_token_transfer.html.eex:4 +#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4 #: lib/block_scout_web/views/transaction_view.ex:116 msgid "Token Transfer" @@ -743,7 +742,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_transaction/_transaction.html.eex:64 -#: lib/block_scout_web/templates/token/show.html.eex:20 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:18 msgid "Transfers" msgstr "" @@ -771,20 +770,23 @@ msgstr "" msgid "Validated Transactions" msgstr "" -#: lib/block_scout_web/templates/token/show.html.eex:95 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:64 msgid "There are no transfers for this Token." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:11 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:10 msgid "Token Details" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:63 -#: lib/block_scout_web/templates/token/show.html.eex:73 -#: lib/block_scout_web/templates/token/show.html.eex:76 -#: lib/block_scout_web/templates/token/show.html.eex:86 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:17 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:34 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:37 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:17 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:36 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:39 +#: lib/block_scout_web/templates/tokens/token/show.html.eex:55 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:12 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:42 #: lib/block_scout_web/templates/transaction_log/index.html.eex:13 @@ -797,17 +799,17 @@ msgid "Token Transfers" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:19 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:17 msgid "addresses" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:22 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:20 msgid "decimals" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/token/show.html.eex:33 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:31 msgid "Total Supply" msgstr "" @@ -817,11 +819,12 @@ msgid "API" msgstr "" #, elixir-format -#: lib/block_scout_web/views/token_helpers.ex:38 -msgid "ERC-721 TokenID [%{token_id}]" +#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:65 +msgid "There are no token transfers for this transaction." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:65 -msgid "There are no token transfers for this transaction." +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:45 +#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:52 +msgid "loading..." msgstr "" diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs index fb6c22a95e..1a666a734c 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs @@ -1,24 +1,43 @@ defmodule BlockScoutWeb.AddressReadContractControllerTest do use BlockScoutWeb.ConnCase - describe "GET show/3" do - test "only responds to ajax requests", %{conn: conn} do - smart_contract = insert(:smart_contract) - - path = - address_read_contract_path( - BlockScoutWeb.Endpoint, - :show, - :en, - smart_contract.address_hash, - smart_contract.address_hash, - function_name: "get", - args: [] - ) - - conn = get(conn, path) - - assert conn.status == 404 + alias Explorer.ExchangeRates.Token + + describe "GET index/3" do + test "with invalid address hash", %{conn: conn} do + conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, :en, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with valid address that is not a contract", %{conn: conn} do + address = insert(:address) + + conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, :en, address.hash)) + + assert html_response(conn, 404) + end + + test "successfully renders the page when the address is a contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address + ) + + insert(:smart_contract, address_hash: contract_address.hash) + + conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, :en, contract_address.hash)) + + assert html_response(conn, 200) + assert contract_address.hash == conn.assigns.address.hash + assert %Token{} = conn.assigns.exchange_rate + assert conn.assigns.transaction_count end end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs new file mode 100644 index 0000000000..4190080bfc --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs @@ -0,0 +1,97 @@ +defmodule BlockScoutWeb.SmartContractControllerTest do + use BlockScoutWeb.ConnCase + + import Mox + + setup :verify_on_exit! + + describe "GET index/3" do + test "only responds to ajax requests", %{conn: conn} do + smart_contract = insert(:smart_contract) + + path = smart_contract_path(BlockScoutWeb.Endpoint, :index, :en, hash: smart_contract.address_hash) + + conn = get(conn, path) + + assert conn.status == 404 + end + + test "lists the smart contract read only functions" do + token_contract_address = insert(:contract_address) + + insert(:smart_contract, address_hash: token_contract_address.hash) + + blockchain_get_function_mock() + + path = smart_contract_path(BlockScoutWeb.Endpoint, :index, :en, hash: token_contract_address.hash) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 200 + refute conn.assigns.read_only_functions == [] + end + end + + describe "GET show/3" do + test "only responds to ajax requests", %{conn: conn} do + smart_contract = insert(:smart_contract) + + path = + smart_contract_path( + BlockScoutWeb.Endpoint, + :show, + :en, + smart_contract.address_hash, + function_name: "get", + args: [] + ) + + conn = get(conn, path) + + assert conn.status == 404 + end + + test "fetch the function value from the blockchain" do + smart_contract = insert(:smart_contract) + + blockchain_get_function_mock() + + path = + smart_contract_path( + BlockScoutWeb.Endpoint, + :show, + :en, + smart_contract.address_hash, + function_name: "get", + args: [] + ) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 200 + + assert %{ + function_name: "get", + layout: false, + locale: "en", + outputs: [%{"name" => "", "type" => "uint256", "value" => 0}] + } = conn.assigns + end + end + + defp blockchain_get_function_mock() do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: _, params: [%{data: _, to: _}]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0000000000000000000000000000000000000000000000000000000000000000"}]} + end + ) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs new file mode 100644 index 0000000000..d4f11f5c8b --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs @@ -0,0 +1,37 @@ +defmodule BlockScoutWeb.Tokens.ReadContractControllerTest do + use BlockScoutWeb.ConnCase + + describe "GET index/3" do + test "with invalid address hash", %{conn: conn} do + conn = get(conn, token_read_contract_path(BlockScoutWeb.Endpoint, :index, :en, "invalid_address")) + + assert html_response(conn, 404) + end + + test "successfully renders the page when the token is a verified smart contract", %{conn: conn} do + token_contract_address = insert(:contract_address) + + token = insert(:token, contract_address: token_contract_address) + + transaction = + :transaction + |> insert() + |> with_block() + + insert( + :token_transfer, + to_address: build(:address), + transaction: transaction, + token_contract_address: token_contract_address, + token: token + ) + + conn = get(conn, token_read_contract_path(BlockScoutWeb.Endpoint, :index, :en, token.contract_address_hash)) + + assert html_response(conn, 200) + assert token.contract_address_hash == conn.assigns.token.contract_address_hash + assert conn.assigns.total_token_transfers + assert conn.assigns.total_address_in_token_transfers + end + end +end diff --git a/apps/block_scout_web/test/test_helper.exs b/apps/block_scout_web/test/test_helper.exs index 0158d8f7c9..59660277e6 100644 --- a/apps/block_scout_web/test/test_helper.exs +++ b/apps/block_scout_web/test/test_helper.exs @@ -15,3 +15,5 @@ ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter]) ExUnit.start() Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, :manual) + +Mox.defmock(EthereumJSONRPC.Mox, for: EthereumJSONRPC.Transport)