diff --git a/apps/explorer/lib/explorer/smart_contract/reader.ex b/apps/explorer/lib/explorer/smart_contract/reader.ex index 788d8d4242..e40ccb171e 100644 --- a/apps/explorer/lib/explorer/smart_contract/reader.ex +++ b/apps/explorer/lib/explorer/smart_contract/reader.ex @@ -65,25 +65,25 @@ defmodule Explorer.SmartContract.Reader do $ Explorer.SmartContract.Reader.read_only_functions("0x798465571ae21a184a272f044f991ad1d5f87a3f") => [ - %{ - "constant" => true, - "inputs" => [], - "name" => "get", - "outputs" => [%{"name" => "", "type" => "uint256", "value" => 0}], - "payable" => false, - "stateMutability" => "view", - "type" => "function" - }, - %{ - "constant" => true, - "inputs" => [%{"name" => "x", "type" => "uint256"}], - "name" => "with_arguments", - "outputs" => [%{"name" => "", "type" => "bool", "value" => ""}], - "payable" => false, - "stateMutability" => "view", - "type" => "function" - } - ] + %{ + "constant" => true, + "inputs" => [], + "name" => "get", + "outputs" => [%{"name" => "", "type" => "uint256", "value" => 0}], + "payable" => false, + "stateMutability" => "view", + "type" => "function" + }, + %{ + "constant" => true, + "inputs" => [%{"name" => "x", "type" => "uint256"}], + "name" => "with_arguments", + "outputs" => [%{"name" => "", "type" => "bool", "value" => ""}], + "payable" => false, + "stateMutability" => "view", + "type" => "function" + } + ] """ @spec read_only_functions(%Explorer.Chain.Hash{}) :: [%{}] def read_only_functions(contract_address_hash) do @@ -140,7 +140,7 @@ defmodule Explorer.SmartContract.Reader do defp fetch_from_blockchain(contract_address_hash, %{name: name, args: args, outputs: outputs}) do contract_address_hash - |> query_contract(%{name => args}) + |> query_contract(%{name => normalize_args(args)}) |> link_outputs_and_values(outputs, name) end diff --git a/apps/explorer_web/assets/css/app.scss b/apps/explorer_web/assets/css/app.scss index 668ff54d93..f7d4af038c 100644 --- a/apps/explorer_web/assets/css/app.scss +++ b/apps/explorer_web/assets/css/app.scss @@ -32,6 +32,7 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "node_modules/bootstrap/scss/utilities/text"; @import "node_modules/bootstrap/scss/utilities/background"; @import "node_modules/bootstrap/scss/utilities/position"; +@import "node_modules/bootstrap/scss/utilities/borders"; // Bootstrap Components @import "node_modules/bootstrap/scss/dropdown"; diff --git a/apps/explorer_web/assets/js/app.js b/apps/explorer_web/assets/js/app.js index 5b071039f0..f14cd6d42d 100644 --- a/apps/explorer_web/assets/js/app.js +++ b/apps/explorer_web/assets/js/app.js @@ -23,6 +23,7 @@ import './lib/from_now' import './lib/market_history_chart' import './lib/reload_button' import './lib/tooltip' +import './lib/smart_contract/read_function' import './pages/address' import './pages/transaction' diff --git a/apps/explorer_web/assets/js/lib/smart_contract/read_function.js b/apps/explorer_web/assets/js/lib/smart_contract/read_function.js new file mode 100644 index 0000000000..a80e05cae9 --- /dev/null +++ b/apps/explorer_web/assets/js/lib/smart_contract/read_function.js @@ -0,0 +1,31 @@ +import $ from 'jquery' + +const readFunction = (element) => { + const $element = $(element) + const $form = $element.find('[data-function-form]') + + const $responseContainer = $element.find('[data-function-response]') + + $form.on('submit', (event) => { + event.preventDefault() + + const url = $form.data('url') + const $functionName = $form.find('input[name=function_name]') + const $functionInputs = $form.find('input[name=function_input]') + + const args = $.map($functionInputs, element => { + return $(element).val() + }) + + const data = { + function_name: $functionName.val(), + args + } + + $.get(url, data, response => $responseContainer.html(response)) + }) +} + +$('[data-function]').each((_, element) => { + readFunction(element) +}) diff --git a/apps/explorer_web/assets/package-lock.json b/apps/explorer_web/assets/package-lock.json index e19242620b..5140c4142e 100644 --- a/apps/explorer_web/assets/package-lock.json +++ b/apps/explorer_web/assets/package-lock.json @@ -11468,7 +11468,7 @@ "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", "dev": true, "requires": { "nice-try": "^1.0.4", diff --git a/apps/explorer_web/config/test.exs b/apps/explorer_web/config/test.exs index dc2523f19b..1eadb09989 100644 --- a/apps/explorer_web/config/test.exs +++ b/apps/explorer_web/config/test.exs @@ -10,4 +10,4 @@ config :explorer_web, ExplorerWeb.Endpoint, server: true # Configure wallaby -config :wallaby, screenshot_on_failure: true +config :wallaby, screenshot_on_failure: true, js_errors: false diff --git a/apps/explorer_web/lib/explorer_web/controllers/address_read_contract_controller.ex b/apps/explorer_web/lib/explorer_web/controllers/address_read_contract_controller.ex new file mode 100644 index 0000000000..2879e9eca0 --- /dev/null +++ b/apps/explorer_web/lib/explorer_web/controllers/address_read_contract_controller.ex @@ -0,0 +1,60 @@ +defmodule ExplorerWeb.AddressReadContractController do + use ExplorerWeb, :controller + + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + alias Explorer.SmartContract.Reader + + import ExplorerWeb.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) + ) + else + :error -> + not_found(conn) + + {:error, :not_found} -> + 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/explorer_web/lib/explorer_web/router.ex b/apps/explorer_web/lib/explorer_web/router.ex index e253e822e3..a2ac976daf 100644 --- a/apps/explorer_web/lib/explorer_web/router.ex +++ b/apps/explorer_web/lib/explorer_web/router.ex @@ -85,6 +85,13 @@ defmodule ExplorerWeb.Router do only: [:new, :create], as: :verify_contract ) + + resources( + "/read_contract", + AddressReadContractController, + only: [:index, :show], + as: :read_contract + ) end get("/search", ChainController, :search) diff --git a/apps/explorer_web/lib/explorer_web/templates/address_contract/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/address_contract/index.html.eex index aeba2becf1..a0683e197c 100644 --- a/apps/explorer_web/lib/explorer_web/templates/address_contract/index.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/address_contract/index.html.eex @@ -31,6 +31,14 @@ <% end %> <% end %> + <%= if smart_contract_with_read_only_functions?(@address) do %> + + <% end %> diff --git a/apps/explorer_web/lib/explorer_web/templates/address_internal_transaction/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/address_internal_transaction/index.html.eex index 9b62af2122..ac59c248ac 100644 --- a/apps/explorer_web/lib/explorer_web/templates/address_internal_transaction/index.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/address_internal_transaction/index.html.eex @@ -36,6 +36,14 @@ <% end %> <% end %> + <%= if smart_contract_with_read_only_functions?(@address) do %> + + <% end %> diff --git a/apps/explorer_web/lib/explorer_web/templates/address_read_contract/_function_response.html.eex b/apps/explorer_web/lib/explorer_web/templates/address_read_contract/_function_response.html.eex new file mode 100644 index 0000000000..11dcd737ea --- /dev/null +++ b/apps/explorer_web/lib/explorer_web/templates/address_read_contract/_function_response.html.eex @@ -0,0 +1,7 @@ + + [ <%= @function_name %> method Response ]
+ + <%= for item <- @outputs do %> + <%= item["type"] %> : <%= item["value"] %> + <% end %> +
diff --git a/apps/explorer_web/lib/explorer_web/templates/address_read_contract/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/address_read_contract/index.html.eex new file mode 100644 index 0000000000..2ed7869aa8 --- /dev/null +++ b/apps/explorer_web/lib/explorer_web/templates/address_read_contract/index.html.eex @@ -0,0 +1,99 @@ +
+ + <%= render ExplorerWeb.AddressView, "overview.html", assigns %> + +
+
+ +
+ +
+

+ + <%= 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 %> + + +
+ <% 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 %> + +
+ <%= if (queryable?(function["inputs"])), do: raw "↳" %> + + <%= for output <- function["outputs"] do %> + <%= output["type"] %> + <% end %> +
+ +
+
+ <% end %> +
+
+
diff --git a/apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex b/apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex index 146de72031..4045199c3b 100644 --- a/apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex @@ -36,6 +36,14 @@ <% end %> <% end %> + <%= if smart_contract_with_read_only_functions?(@address) do %> + + <% end %> diff --git a/apps/explorer_web/lib/explorer_web/views/address_contract_view.ex b/apps/explorer_web/lib/explorer_web/views/address_contract_view.ex index 8fc6049072..47db821975 100644 --- a/apps/explorer_web/lib/explorer_web/views/address_contract_view.ex +++ b/apps/explorer_web/lib/explorer_web/views/address_contract_view.ex @@ -1,7 +1,7 @@ defmodule ExplorerWeb.AddressContractView do use ExplorerWeb, :view - import ExplorerWeb.AddressView, only: [smart_contract_verified?: 1] + import ExplorerWeb.AddressView, only: [smart_contract_verified?: 1, smart_contract_with_read_only_functions?: 1] def format_smart_contract_abi(abi), do: Poison.encode!(abi, pretty: true) diff --git a/apps/explorer_web/lib/explorer_web/views/address_internal_transaction_view.ex b/apps/explorer_web/lib/explorer_web/views/address_internal_transaction_view.ex index ddcd7f9ea4..567e261085 100644 --- a/apps/explorer_web/lib/explorer_web/views/address_internal_transaction_view.ex +++ b/apps/explorer_web/lib/explorer_web/views/address_internal_transaction_view.ex @@ -1,7 +1,8 @@ defmodule ExplorerWeb.AddressInternalTransactionView do use ExplorerWeb, :view - import ExplorerWeb.AddressView, only: [contract?: 1, smart_contract_verified?: 1] + import ExplorerWeb.AddressView, + only: [contract?: 1, smart_contract_verified?: 1, smart_contract_with_read_only_functions?: 1] def format_current_filter(filter) do case filter do diff --git a/apps/explorer_web/lib/explorer_web/views/address_read_contract_view.ex b/apps/explorer_web/lib/explorer_web/views/address_read_contract_view.ex new file mode 100644 index 0000000000..bbdf9f0a72 --- /dev/null +++ b/apps/explorer_web/lib/explorer_web/views/address_read_contract_view.ex @@ -0,0 +1,9 @@ +defmodule ExplorerWeb.AddressReadContractView do + use ExplorerWeb, :view + + import ExplorerWeb.AddressView, only: [smart_contract_verified?: 1] + + def queryable?(inputs), do: Enum.any?(inputs) + + def address?(type), do: type == "address" +end diff --git a/apps/explorer_web/lib/explorer_web/views/address_transaction_view.ex b/apps/explorer_web/lib/explorer_web/views/address_transaction_view.ex index 9436597f8f..89531c532d 100644 --- a/apps/explorer_web/lib/explorer_web/views/address_transaction_view.ex +++ b/apps/explorer_web/lib/explorer_web/views/address_transaction_view.ex @@ -1,7 +1,8 @@ defmodule ExplorerWeb.AddressTransactionView do use ExplorerWeb, :view - import ExplorerWeb.AddressView, only: [contract?: 1, smart_contract_verified?: 1] + import ExplorerWeb.AddressView, + only: [contract?: 1, smart_contract_verified?: 1, smart_contract_with_read_only_functions?: 1] def format_current_filter(filter) do case filter do diff --git a/apps/explorer_web/lib/explorer_web/views/address_view.ex b/apps/explorer_web/lib/explorer_web/views/address_view.ex index cde119d3b7..0819e057fb 100644 --- a/apps/explorer_web/lib/explorer_web/views/address_view.ex +++ b/apps/explorer_web/lib/explorer_web/views/address_view.ex @@ -72,4 +72,10 @@ defmodule ExplorerWeb.AddressView do end def trimmed_hash(_), do: "" + + def smart_contract_with_read_only_functions?(%Address{smart_contract: %SmartContract{}} = address) do + Enum.any?(address.smart_contract.abi, & &1["constant"]) + end + + def smart_contract_with_read_only_functions?(%Address{smart_contract: nil}), do: false end diff --git a/apps/explorer_web/package-lock.json b/apps/explorer_web/package-lock.json new file mode 100644 index 0000000000..48e341a095 --- /dev/null +++ b/apps/explorer_web/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/apps/explorer_web/priv/gettext/default.pot b/apps/explorer_web/priv/gettext/default.pot index 69b77e6679..1fcd3a2dfb 100644 --- a/apps/explorer_web/priv/gettext/default.pot +++ b/apps/explorer_web/priv/gettext/default.pot @@ -40,10 +40,11 @@ msgstr "" #: lib/explorer_web/templates/address/overview.html.eex:21 #: lib/explorer_web/templates/address_contract/index.html.eex:10 #: lib/explorer_web/templates/address_internal_transaction/index.html.eex:13 -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:47 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:55 +#: lib/explorer_web/templates/address_read_contract/index.html.eex:10 #: lib/explorer_web/templates/address_transaction/index.html.eex:13 -#: lib/explorer_web/templates/address_transaction/index.html.eex:47 -#: lib/explorer_web/templates/address_transaction/index.html.eex:123 +#: lib/explorer_web/templates/address_transaction/index.html.eex:55 +#: lib/explorer_web/templates/address_transaction/index.html.eex:131 #: lib/explorer_web/templates/block/index.html.eex:19 #: lib/explorer_web/templates/block_transaction/index.html.eex:124 #: lib/explorer_web/templates/chain/_transactions.html.eex:4 @@ -142,13 +143,13 @@ msgstr "" msgid "Address" msgstr "" -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:98 -#: lib/explorer_web/templates/address_transaction/index.html.eex:109 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:106 +#: lib/explorer_web/templates/address_transaction/index.html.eex:117 #: lib/explorer_web/templates/block_transaction/index.html.eex:142 #: lib/explorer_web/templates/transaction/overview.html.eex:79 #: lib/explorer_web/templates/transaction_internal_transaction/index.html.eex:31 -#: lib/explorer_web/views/address_internal_transaction_view.ex:9 -#: lib/explorer_web/views/address_transaction_view.ex:9 +#: lib/explorer_web/views/address_internal_transaction_view.ex:10 +#: lib/explorer_web/views/address_transaction_view.ex:10 msgid "From" msgstr "" @@ -161,13 +162,13 @@ msgstr "" msgid "Success" msgstr "" -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:86 -#: lib/explorer_web/templates/address_transaction/index.html.eex:97 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:94 +#: lib/explorer_web/templates/address_transaction/index.html.eex:105 #: lib/explorer_web/templates/block_transaction/index.html.eex:144 #: lib/explorer_web/templates/transaction/overview.html.eex:91 #: lib/explorer_web/templates/transaction_internal_transaction/index.html.eex:32 -#: lib/explorer_web/views/address_internal_transaction_view.ex:8 -#: lib/explorer_web/views/address_transaction_view.ex:8 +#: lib/explorer_web/views/address_internal_transaction_view.ex:9 +#: lib/explorer_web/views/address_transaction_view.ex:9 msgid "To" msgstr "" @@ -312,10 +313,11 @@ msgstr "" #: lib/explorer_web/templates/address_contract/index.html.eex:17 #: lib/explorer_web/templates/address_internal_transaction/index.html.eex:20 -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:52 -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:111 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:60 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:119 +#: lib/explorer_web/templates/address_read_contract/index.html.eex:17 #: lib/explorer_web/templates/address_transaction/index.html.eex:20 -#: lib/explorer_web/templates/address_transaction/index.html.eex:52 +#: lib/explorer_web/templates/address_transaction/index.html.eex:60 #: lib/explorer_web/templates/transaction_internal_transaction/index.html.eex:11 #: lib/explorer_web/templates/transaction_log/index.html.eex:11 msgid "Internal Transactions" @@ -353,10 +355,10 @@ msgstr "" msgid "Wei" msgstr "" -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:80 -#: lib/explorer_web/templates/address_transaction/index.html.eex:91 -#: lib/explorer_web/views/address_internal_transaction_view.ex:10 -#: lib/explorer_web/views/address_transaction_view.ex:10 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:88 +#: lib/explorer_web/templates/address_transaction/index.html.eex:99 +#: lib/explorer_web/views/address_internal_transaction_view.ex:11 +#: lib/explorer_web/views/address_transaction_view.ex:11 msgid "All" msgstr "" @@ -473,8 +475,8 @@ msgstr "" msgid "There are no Transactions" msgstr "" -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:125 -#: lib/explorer_web/templates/address_transaction/index.html.eex:138 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:133 +#: lib/explorer_web/templates/address_transaction/index.html.eex:146 #: lib/explorer_web/templates/block/index.html.eex:60 #: lib/explorer_web/templates/block_transaction/index.html.eex:198 #: lib/explorer_web/templates/pending_transaction/index.html.eex:54 @@ -486,15 +488,16 @@ msgstr "" msgid "Contract Source Code" msgstr "" -#: lib/explorer_web/templates/address_contract/index.html.eex:44 +#: lib/explorer_web/templates/address_contract/index.html.eex:52 msgid "Verify and Publish" msgstr "" #: lib/explorer_web/templates/address_contract/index.html.eex:27 #: lib/explorer_web/templates/address_internal_transaction/index.html.eex:31 -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:61 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:69 +#: lib/explorer_web/templates/address_read_contract/index.html.eex:27 #: lib/explorer_web/templates/address_transaction/index.html.eex:31 -#: lib/explorer_web/templates/address_transaction/index.html.eex:61 +#: lib/explorer_web/templates/address_transaction/index.html.eex:69 msgid "Code" msgstr "" @@ -589,7 +592,7 @@ msgid "block confirmations" msgstr "" #, elixir-format -#: lib/explorer_web/templates/address_transaction/index.html.eex:81 +#: lib/explorer_web/templates/address_transaction/index.html.eex:89 msgid "Connection Lost, click to load newer transactions" msgstr "" @@ -605,7 +608,7 @@ msgid "Internal Transaction" msgstr "" #, elixir-format -#: lib/explorer_web/templates/address_transaction/index.html.eex:76 +#: lib/explorer_web/templates/address_transaction/index.html.eex:84 msgid "More messages have come in" msgstr "" @@ -615,12 +618,12 @@ msgid "QR Code" msgstr "" #, elixir-format -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:118 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:126 msgid "There are no internal transactions for this address." msgstr "" #, elixir-format -#: lib/explorer_web/templates/address_transaction/index.html.eex:131 +#: lib/explorer_web/templates/address_transaction/index.html.eex:139 msgid "There are no transactions for this address." msgstr "" @@ -664,3 +667,21 @@ msgstr "" #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:29 msgid "OUT" msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/address_read_contract/index.html.eex:69 +msgid "Query" +msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/address_contract/index.html.eex:37 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:42 +#: lib/explorer_web/templates/address_read_contract/index.html.eex:36 +#: lib/explorer_web/templates/address_transaction/index.html.eex:42 +msgid "Read Contract" +msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/address_read_contract/index.html.eex:46 +msgid "Read Contract Information" +msgstr "" diff --git a/apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po index a5f8d932e7..b4436e34df 100644 --- a/apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po @@ -52,10 +52,11 @@ msgstr "POA Network Explorer" #: lib/explorer_web/templates/address/overview.html.eex:21 #: lib/explorer_web/templates/address_contract/index.html.eex:10 #: lib/explorer_web/templates/address_internal_transaction/index.html.eex:13 -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:47 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:55 +#: lib/explorer_web/templates/address_read_contract/index.html.eex:10 #: lib/explorer_web/templates/address_transaction/index.html.eex:13 -#: lib/explorer_web/templates/address_transaction/index.html.eex:47 -#: lib/explorer_web/templates/address_transaction/index.html.eex:123 +#: lib/explorer_web/templates/address_transaction/index.html.eex:55 +#: lib/explorer_web/templates/address_transaction/index.html.eex:131 #: lib/explorer_web/templates/block/index.html.eex:19 #: lib/explorer_web/templates/block_transaction/index.html.eex:124 #: lib/explorer_web/templates/chain/_transactions.html.eex:4 @@ -154,13 +155,13 @@ msgstr "%{count} transactions in this block" msgid "Address" msgstr "Address" -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:98 -#: lib/explorer_web/templates/address_transaction/index.html.eex:109 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:106 +#: lib/explorer_web/templates/address_transaction/index.html.eex:117 #: lib/explorer_web/templates/block_transaction/index.html.eex:142 #: lib/explorer_web/templates/transaction/overview.html.eex:79 #: lib/explorer_web/templates/transaction_internal_transaction/index.html.eex:31 -#: lib/explorer_web/views/address_internal_transaction_view.ex:9 -#: lib/explorer_web/views/address_transaction_view.ex:9 +#: lib/explorer_web/views/address_internal_transaction_view.ex:10 +#: lib/explorer_web/views/address_transaction_view.ex:10 msgid "From" msgstr "From" @@ -173,13 +174,13 @@ msgstr "Overview" msgid "Success" msgstr "Success" -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:86 -#: lib/explorer_web/templates/address_transaction/index.html.eex:97 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:94 +#: lib/explorer_web/templates/address_transaction/index.html.eex:105 #: lib/explorer_web/templates/block_transaction/index.html.eex:144 #: lib/explorer_web/templates/transaction/overview.html.eex:91 #: lib/explorer_web/templates/transaction_internal_transaction/index.html.eex:32 -#: lib/explorer_web/views/address_internal_transaction_view.ex:8 -#: lib/explorer_web/views/address_transaction_view.ex:8 +#: lib/explorer_web/views/address_internal_transaction_view.ex:9 +#: lib/explorer_web/views/address_transaction_view.ex:9 msgid "To" msgstr "To" @@ -324,10 +325,11 @@ msgstr "" #: lib/explorer_web/templates/address_contract/index.html.eex:17 #: lib/explorer_web/templates/address_internal_transaction/index.html.eex:20 -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:52 -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:111 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:60 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:119 +#: lib/explorer_web/templates/address_read_contract/index.html.eex:17 #: lib/explorer_web/templates/address_transaction/index.html.eex:20 -#: lib/explorer_web/templates/address_transaction/index.html.eex:52 +#: lib/explorer_web/templates/address_transaction/index.html.eex:60 #: lib/explorer_web/templates/transaction_internal_transaction/index.html.eex:11 #: lib/explorer_web/templates/transaction_log/index.html.eex:11 msgid "Internal Transactions" @@ -365,10 +367,10 @@ msgstr "" msgid "Wei" msgstr "" -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:80 -#: lib/explorer_web/templates/address_transaction/index.html.eex:91 -#: lib/explorer_web/views/address_internal_transaction_view.ex:10 -#: lib/explorer_web/views/address_transaction_view.ex:10 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:88 +#: lib/explorer_web/templates/address_transaction/index.html.eex:99 +#: lib/explorer_web/views/address_internal_transaction_view.ex:11 +#: lib/explorer_web/views/address_transaction_view.ex:11 msgid "All" msgstr "" @@ -485,8 +487,8 @@ msgstr "" msgid "There are no Transactions" msgstr "" -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:125 -#: lib/explorer_web/templates/address_transaction/index.html.eex:138 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:133 +#: lib/explorer_web/templates/address_transaction/index.html.eex:146 #: lib/explorer_web/templates/block/index.html.eex:60 #: lib/explorer_web/templates/block_transaction/index.html.eex:198 #: lib/explorer_web/templates/pending_transaction/index.html.eex:54 @@ -498,15 +500,16 @@ msgstr "" msgid "Contract Source Code" msgstr "Contract Source Code" -#: lib/explorer_web/templates/address_contract/index.html.eex:44 +#: lib/explorer_web/templates/address_contract/index.html.eex:52 msgid "Verify and Publish" msgstr "" #: lib/explorer_web/templates/address_contract/index.html.eex:27 #: lib/explorer_web/templates/address_internal_transaction/index.html.eex:31 -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:61 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:69 +#: lib/explorer_web/templates/address_read_contract/index.html.eex:27 #: lib/explorer_web/templates/address_transaction/index.html.eex:31 -#: lib/explorer_web/templates/address_transaction/index.html.eex:61 +#: lib/explorer_web/templates/address_transaction/index.html.eex:69 msgid "Code" msgstr "" @@ -601,7 +604,7 @@ msgid "block confirmations" msgstr "" #, elixir-format -#: lib/explorer_web/templates/address_transaction/index.html.eex:81 +#: lib/explorer_web/templates/address_transaction/index.html.eex:89 msgid "Connection Lost, click to load newer transactions" msgstr "" @@ -617,7 +620,7 @@ msgid "Internal Transaction" msgstr "" #, elixir-format -#: lib/explorer_web/templates/address_transaction/index.html.eex:76 +#: lib/explorer_web/templates/address_transaction/index.html.eex:84 msgid "More messages have come in" msgstr "" @@ -627,12 +630,12 @@ msgid "QR Code" msgstr "" #, elixir-format -#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:118 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:126 msgid "There are no internal transactions for this address." msgstr "" #, elixir-format -#: lib/explorer_web/templates/address_transaction/index.html.eex:131 +#: lib/explorer_web/templates/address_transaction/index.html.eex:139 msgid "There are no transactions for this address." msgstr "" @@ -656,7 +659,7 @@ msgstr "" msgid "at" msgstr "" -#, elixir-format, fuzzy +#, elixir-format #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:36 #: lib/explorer_web/templates/chain/_transactions.html.eex:25 #: lib/explorer_web/templates/transaction/index.html.eex:47 @@ -676,3 +679,21 @@ msgstr "" #: lib/explorer_web/templates/address_transaction/_transaction.html.eex:29 msgid "OUT" msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/address_read_contract/index.html.eex:69 +msgid "Query" +msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/address_contract/index.html.eex:37 +#: lib/explorer_web/templates/address_internal_transaction/index.html.eex:42 +#: lib/explorer_web/templates/address_read_contract/index.html.eex:36 +#: lib/explorer_web/templates/address_transaction/index.html.eex:42 +msgid "Read Contract" +msgstr "" + +#, elixir-format +#: lib/explorer_web/templates/address_read_contract/index.html.eex:46 +msgid "Read Contract Information" +msgstr "" diff --git a/apps/explorer_web/test/explorer_web/controllers/address_read_contract_controller_test.exs b/apps/explorer_web/test/explorer_web/controllers/address_read_contract_controller_test.exs new file mode 100644 index 0000000000..885c810ac7 --- /dev/null +++ b/apps/explorer_web/test/explorer_web/controllers/address_read_contract_controller_test.exs @@ -0,0 +1,24 @@ +defmodule ExplorerWeb.AddressReadContractControllerTest do + use ExplorerWeb.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( + ExplorerWeb.Endpoint, + :show, + :en, + smart_contract.address_hash, + smart_contract.address_hash, + function_name: "get", + args: [] + ) + + conn = get(conn, path) + + assert conn.status == 404 + end + end +end diff --git a/apps/explorer_web/test/explorer_web/views/address_read_contract_view_test.exs b/apps/explorer_web/test/explorer_web/views/address_read_contract_view_test.exs new file mode 100644 index 0000000000..ef68d5833b --- /dev/null +++ b/apps/explorer_web/test/explorer_web/views/address_read_contract_view_test.exs @@ -0,0 +1,19 @@ +defmodule ExplorerWeb.AddressReadContractViewTest do + use ExplorerWeb.ConnCase, async: true + + alias ExplorerWeb.AddressReadContractView + + describe "queryable?/1" do + test "returns true if list of inputs is not empty" do + assert AddressReadContractView.queryable?([%{"name" => "argument_name", "type" => "uint256"}]) == true + assert AddressReadContractView.queryable?([]) == false + end + end + + describe "address?/1" do + test "returns true if type equals `address`" do + assert AddressReadContractView.address?("address") == true + assert AddressReadContractView.address?("uint256") == false + end + end +end diff --git a/apps/explorer_web/test/explorer_web/views/address_view_test.exs b/apps/explorer_web/test/explorer_web/views/address_view_test.exs index a196c5e875..47c3c5f13a 100644 --- a/apps/explorer_web/test/explorer_web/views/address_view_test.exs +++ b/apps/explorer_web/test/explorer_web/views/address_view_test.exs @@ -44,4 +44,71 @@ defmodule ExplorerWeb.AddressViewTest do assert {:ok, _} = Base.decode64(AddressView.qr_code(address)) end end + + describe "smart_contract_verified?/1" do + test "returns true when smart contract is verified" do + smart_contract = insert(:smart_contract) + address = insert(:address, smart_contract: smart_contract) + + assert AddressView.smart_contract_verified?(address) + end + + test "returns false when smart contract is not verified" do + address = insert(:address, smart_contract: nil) + + refute AddressView.smart_contract_verified?(address) + end + end + + describe "smart_contract_with_read_only_functions?/1" do + test "returns true when abi has read only functions" do + smart_contract = + insert( + :smart_contract, + abi: [ + %{ + "constant" => true, + "inputs" => [], + "name" => "get", + "outputs" => [%{"name" => "", "type" => "uint256"}], + "payable" => false, + "stateMutability" => "view", + "type" => "function" + } + ] + ) + + address = insert(:address, smart_contract: smart_contract) + + assert AddressView.smart_contract_with_read_only_functions?(address) + end + + test "returns false when there is no read only functions" do + smart_contract = + insert( + :smart_contract, + abi: [ + %{ + "constant" => false, + "inputs" => [%{"name" => "x", "type" => "uint256"}], + "name" => "set", + "outputs" => [], + "payable" => false, + "stateMutability" => "nonpayable", + "type" => "function" + } + ] + ) + + address = insert(:address, smart_contract: smart_contract) + + refute AddressView.smart_contract_with_read_only_functions?(address) + end + + test "returns false when smart contract is not verified" do + address = insert(:address, smart_contract: nil) + + refute AddressView.smart_contract_with_read_only_functions?(address) + end + end end