diff --git a/CHANGELOG.md b/CHANGELOG.md index 7901172443..1460a1af7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,13 @@ ### Features +- [#1895](https://github.com/poanetwork/blockscout/pull/1874) - add changes to poa theme and poa logo +- [#1812](https://github.com/poanetwork/blockscout/pull/1812) - add pagination to addresses page +- [#1874](https://github.com/poanetwork/blockscout/pull/1874) - add changes to ethereum theme and ethereum logo - [#1815](https://github.com/poanetwork/blockscout/pull/1815) - able to search without prefix "0x" - [#1813](https://github.com/poanetwork/blockscout/pull/1813) - add total blocks counter to the main page - [#1806](https://github.com/poanetwork/blockscout/pull/1806) - verify contracts with a post request -- [#1857](https://github.com/poanetwork/blockscout/pull/1857) - Re-implement Geth JS internal transaction tracer in Elixir +- [#1857](https://github.com/poanetwork/blockscout/pull/1857) - Re-implement Geth JS internal transaction tracer in Elixir - [#1859](https://github.com/poanetwork/blockscout/pull/1859) - feat: show raw transaction traces ### Fixes @@ -18,6 +21,8 @@ - [#1869](https://github.com/poanetwork/blockscout/pull/1869) - Fix output and gas extraction in JS tracer for Geth - [#1868](https://github.com/poanetwork/blockscout/pull/1868) - fix: logs list endpoint performance - [#1822](https://github.com/poanetwork/blockscout/pull/1822) - Fix style breaks in decompiled contract code view +- [#1896](https://github.com/poanetwork/blockscout/pull/1896) - re-query tokens in top nav automplete +- [#1881](https://github.com/poanetwork/blockscout/pull/1881) - fix: store solc versions locally for performance ### Chore diff --git a/README.md b/README.md index a2df24f85c..289a3d35ed 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ The [development stack page](https://github.com/poanetwork/blockscout/wiki/Devel ``` * If using Chrome, Enable `chrome://flags/#allow-insecure-localhost`. - 9. Start Phoenix Server. + 9. Run the Phoenix Server from the root directory of your application. `mix phx.server` Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss b/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss index 98d2da802c..7437274bf6 100644 --- a/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss @@ -1,3 +1,50 @@ -$primary: #16465b; -$secondary: #5ab3ff; -$tertiary: #77a4c5; +// general +$primary: #153550; +$secondary: #49a2ee; +$tertiary: #4ad7a7; +$additional-font: #89cae6; + +// footer +$footer-background-color: $primary; +$footer-title-color: #fff; +$footer-text-color: #89cae6; +$footer-item-disc-color: $secondary; +.footer-logo { filter: brightness(0) invert(1); } + +// dashboard +$dashboard-line-color-price: $tertiary; // price left border + +$dashboard-banner-chart-legend-value-color: $additional-font; // chart labels + +$dashboard-stats-item-value-color: $additional-font; // stat values + +$dashboard-stats-item-border-color: $secondary; // stat border + +$dashboard-banner-gradient-start: $primary; // gradient begin + +$dashboard-banner-gradient-end: lighten($primary, 5); // gradient end + +$dashboard-banner-network-plain-container-background-color: #1c476c; // stats bg + + +// navigation +.navbar { box-shadow: 0px 0px 30px 0px rgba(21, 53, 80, 0.12); } // header shadow +$header-icon-border-color-hover: $secondary; // top border on hover +$header-icon-color-hover: $secondary; // nav icon on hover +.dropdown-item:hover, .dropdown-item:focus { background-color: $secondary !important; } // dropdown item on hover + +// buttons +$btn-line-bg: #fff; // button bg +$btn-line-color: $secondary; // button border and font color && hover bg color +$btn-copy-color: $secondary; // btn copy +$btn-qr-color: $secondary; // btn qr-code + +//links & tile +.tile a { color: $secondary !important; } // links color for badges +.tile-type-block { + border-left: 4px solid $secondary; +} // tab active bg + +// card +$card-background-1: $secondary; +$card-tab-active: $secondary; diff --git a/apps/block_scout_web/assets/css/theme/_poa_variables.scss b/apps/block_scout_web/assets/css/theme/_poa_variables.scss index da2fe0de74..198c89ba5a 100644 --- a/apps/block_scout_web/assets/css/theme/_poa_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_poa_variables.scss @@ -1,11 +1,50 @@ -$primary: #5b389f; -$secondary: #7dd79f; -$tertiary: #997fdc; +// general +$primary: #5c34a2; +$secondary: #87e1a9; +$tertiary: #bf9cff; +$additional-font: #fff; -$header-links-color-active: #333; -$button-secondary-color: $primary; - -$footer-background-color: $primary; +// footer +$footer-background-color: #3c226a; $footer-title-color: #fff; -$footer-text-color: #fff; +$footer-text-color: #bda6e7; $footer-item-disc-color: $secondary; +.footer-logo { filter: brightness(0) invert(1); } + +// dashboard +$dashboard-line-color-price: $tertiary; // price left border + +$dashboard-banner-chart-legend-value-color: $additional-font; // chart labels + +$dashboard-stats-item-value-color: $additional-font; // stat values + +$dashboard-stats-item-border-color: $secondary; // stat border + +$dashboard-banner-gradient-start: $primary; // gradient begin + +$dashboard-banner-gradient-end: lighten($primary, 5); // gradient end + +$dashboard-banner-network-plain-container-background-color: #865bd4; // stats bg + + +// navigation +.navbar { box-shadow: 0px 0px 30px 0px rgba(21, 53, 80, 0.12); } // header shadow +$header-icon-border-color-hover: $primary; // top border on hover +$header-icon-color-hover: $primary; // nav icon on hover +.dropdown-item:hover, .dropdown-item:focus { background-color: $primary !important; } // dropdown item on hover + +// buttons +$btn-line-bg: #fff; // button bg +$btn-line-color: $primary; // button border and font color && hover bg color +$btn-copy-color: $primary; // btn copy +$btn-qr-color: $primary; // btn qr-code + +//links & tile +.tile a { color: $primary !important; } // links color for badges +.tile-type-block { + border-left: 4px solid $primary; +} // tab active bg + +// card +$card-background-1: $primary; +$card-tab-active: $primary; diff --git a/apps/block_scout_web/assets/static/images/ethereum_logo.svg b/apps/block_scout_web/assets/static/images/ethereum_logo.svg index 3f47dc7fe2..b2ebb795f8 100644 --- a/apps/block_scout_web/assets/static/images/ethereum_logo.svg +++ b/apps/block_scout_web/assets/static/images/ethereum_logo.svg @@ -1,40 +1 @@ - - - - -ethereum-logo - - - - - - - - - - - - - + diff --git a/apps/block_scout_web/assets/static/images/poa_logo.svg b/apps/block_scout_web/assets/static/images/poa_logo.svg index 551871a7e1..321033174f 100644 --- a/apps/block_scout_web/assets/static/images/poa_logo.svg +++ b/apps/block_scout_web/assets/static/images/poa_logo.svg @@ -1 +1,3 @@ -poa_logo \ No newline at end of file + + + diff --git a/apps/block_scout_web/lib/block_scout_web/chain.ex b/apps/block_scout_web/lib/block_scout_web/chain.ex index 0aa33b5ffd..cabac4ae02 100644 --- a/apps/block_scout_web/lib/block_scout_web/chain.ex +++ b/apps/block_scout_web/lib/block_scout_web/chain.ex @@ -25,7 +25,8 @@ defmodule BlockScoutWeb.Chain do InternalTransaction, Log, TokenTransfer, - Transaction + Transaction, + Wei } alias Explorer.PagingOptions @@ -87,6 +88,16 @@ defmodule BlockScoutWeb.Chain do Map.merge(params, paging_params(List.last(list))) end + def paging_options(%{"hash" => hash, "fetched_coin_balance" => fetched_coin_balance}) do + with {coin_balance, ""} <- Integer.parse(fetched_coin_balance), + {:ok, address_hash} <- string_to_address_hash(hash) do + [paging_options: %{@default_paging_options | key: {%Wei{value: Decimal.new(coin_balance)}, address_hash}}] + else + _ -> + [paging_options: @default_paging_options] + end + end + def paging_options(%{ "block_number" => block_number_string, "transaction_index" => transaction_index_string, @@ -177,6 +188,10 @@ defmodule BlockScoutWeb.Chain do end end + defp paging_params({%Address{hash: hash, fetched_coin_balance: fetched_coin_balance}, _}) do + %{"hash" => hash, "fetched_coin_balance" => Decimal.to_string(fetched_coin_balance.value)} + end + defp paging_params({%Reward{block: %{number: number}}, _}) do %{"block_number" => number, "index" => 0} end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index a0c443aee6..3d43ed7054 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -1,16 +1,55 @@ defmodule BlockScoutWeb.AddressController do use BlockScoutWeb, :controller + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + alias Explorer.{Chain, Market} alias Explorer.Chain.Address alias Explorer.ExchangeRates.Token - def index(conn, _params) do + def index(conn, params) do + addresses = + params + |> paging_options() + |> Chain.list_top_addresses() + + {addresses_page, next_page} = split_list_by_page(addresses) + + cur_page_number = + cond do + !params["prev_page_number"] -> 1 + params["next_page"] -> String.to_integer(params["prev_page_number"]) + 1 + params["prev_page"] -> String.to_integer(params["prev_page_number"]) - 1 + end + + next_page_path = + case next_page_params(next_page, addresses_page, params) do + nil -> + nil + + next_page_params -> + next_params = + next_page_params + |> Map.put("prev_page_path", cur_page_path(conn, params)) + |> Map.put("next_page", true) + |> Map.put("prev_page_number", cur_page_number) + + address_path( + conn, + :index, + next_params + ) + end + render(conn, "index.html", - address_tx_count_pairs: Chain.list_top_addresses(), + address_tx_count_pairs: addresses_page, + page_address_count: Enum.count(addresses_page), address_count: Chain.count_addresses_with_balance_from_cache(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), - total_supply: Chain.total_supply() + total_supply: Chain.total_supply(), + next_page_path: next_page_path, + prev_page_path: params["prev_page_path"], + cur_page_number: cur_page_number ) end @@ -25,4 +64,16 @@ defmodule BlockScoutWeb.AddressController do def validation_count(%Address{} = address) do Chain.address_to_validation_count(address) end + + defp cur_page_path(conn, %{"hash" => _hash, "fetched_coin_balance" => _balance} = params) do + new_params = Map.put(params, "next_page", false) + + address_path( + conn, + :index, + new_params + ) + end + + defp cur_page_path(conn, _), do: address_path(conn, :index) end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex index f9bd1b6dcd..fca5d5b50d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex @@ -1,12 +1,28 @@
+ <%= if @next_page_path do %> + " class="button button-secondary button-small float-right ml-1"> + <%= gettext("Next") %> + + <% end %> + <%= if @prev_page_path do %> + " class="button button-secondary button-small float-right"> + <%= gettext("Back") %> + + <% end %> +

<%= gettext "Addresses" %>

- <%= gettext "Showing 250 addresses of" %> + <%= gettext "Showing " %> + <%= Cldr.Number.to_string!(@page_address_count, format: "#,###") %> + <%= gettext " addresses of" %> <%= Cldr.Number.to_string!(@address_count, format: "#,###") %> <%= gettext "total addresses with a balance" %> + <%= gettext " (page" %> + <%= Cldr.Number.to_string!(@cur_page_number, format: "#,###)") %>

+ <%= for {{address, tx_count}, index} <- Enum.with_index(@address_tx_count_pairs, 1) do %> <%= render "_tile.html", @@ -14,6 +30,17 @@ total_supply: @total_supply, tx_count: tx_count, validation_count: validation_count(address) %> <% end %> +
+ <%= if @next_page_path do %> + " class="button button-secondary button-small float-right mt-0 mb-0 ml-1"> + <%= gettext("Next") %> + + <% end %> + <%= if @prev_page_path do %> + " class="button button-secondary button-small float-right mt-0 mb-0"> + <%= gettext("Back") %> + + <% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex index 032ab22930..f7392a6b39 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex @@ -112,9 +112,8 @@ "data-test": "search_input" ], [ url: "#{chain_path(@conn, :token_autocomplete)}?q=", - prepop: true, - minChars: 3, - maxItems: 8, + limit: 0, + minChars: 2, value: "contract_address_hash", label: "contract_address_hash", descrSearch: true, diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 6f198f8793..d38ebaced2 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -104,7 +104,7 @@ msgid "Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/index.html.eex:4 +#: lib/block_scout_web/templates/address/index.html.eex:15 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:59 msgid "Addresses" msgstr "" @@ -578,6 +578,8 @@ msgid "Newer" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:6 +#: lib/block_scout_web/templates/address/index.html.eex:36 #: lib/block_scout_web/templates/address_token/index.html.eex:22 msgid "Next" msgstr "" @@ -712,7 +714,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/layout/_topnav.html.eex:111 -#: lib/block_scout_web/templates/layout/_topnav.html.eex:129 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:128 msgid "Search" msgstr "" @@ -739,11 +741,6 @@ msgstr "" msgid "Showing" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/address/index.html.eex:6 -msgid "Showing 250 addresses of" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 #: lib/block_scout_web/views/transaction_view.ex:210 @@ -1078,7 +1075,7 @@ msgid "string" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/index.html.eex:8 +#: lib/block_scout_web/templates/address/index.html.eex:21 msgid "total addresses with a balance" msgstr "" @@ -1708,11 +1705,6 @@ msgstr "" msgid "ERC-721" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/chain/show.html.eex:64 -msgid "Total blocks" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/api_docs/index.html.eex:4 msgid "API Documentation" @@ -1739,3 +1731,29 @@ msgstr "" #: lib/block_scout_web/views/transaction_view.ex:341 msgid "Raw Trace" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:22 +msgid " (page" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:19 +msgid " addresses of" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:11 +#: lib/block_scout_web/templates/address/index.html.eex:41 +msgid "Back" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:17 +msgid "Showing " +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/chain/show.html.eex:64 +msgid "Total blocks" +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 64201bee65..067af0cd66 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 @@ -104,7 +104,7 @@ msgid "Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/index.html.eex:4 +#: lib/block_scout_web/templates/address/index.html.eex:15 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:59 msgid "Addresses" msgstr "" @@ -578,6 +578,8 @@ msgid "Newer" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:6 +#: lib/block_scout_web/templates/address/index.html.eex:36 #: lib/block_scout_web/templates/address_token/index.html.eex:22 msgid "Next" msgstr "" @@ -712,7 +714,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/layout/_topnav.html.eex:111 -#: lib/block_scout_web/templates/layout/_topnav.html.eex:129 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:128 msgid "Search" msgstr "" @@ -739,11 +741,6 @@ msgstr "" msgid "Showing" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/address/index.html.eex:6 -msgid "Showing 250 addresses of" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 #: lib/block_scout_web/views/transaction_view.ex:210 @@ -1078,7 +1075,7 @@ msgid "string" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/index.html.eex:8 +#: lib/block_scout_web/templates/address/index.html.eex:21 msgid "total addresses with a balance" msgstr "" @@ -1708,11 +1705,6 @@ msgstr "" msgid "ERC-721" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/chain/show.html.eex:64 -msgid "Total blocks" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/api_docs/index.html.eex:4 msgid "API Documentation" @@ -1739,3 +1731,29 @@ msgstr "" #: lib/block_scout_web/views/transaction_view.ex:341 msgid "Raw Trace" msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:22 +msgid " (page" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:19 +msgid " addresses of" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:11 +#: lib/block_scout_web/templates/address/index.html.eex:41 +msgid "Back" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address/index.html.eex:17 +msgid "Showing " +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/chain/show.html.eex:64 +msgid "Total blocks" +msgstr "" diff --git a/apps/explorer/.gitignore b/apps/explorer/.gitignore index f4cd5728cc..bf75b19355 100644 --- a/apps/explorer/.gitignore +++ b/apps/explorer/.gitignore @@ -1 +1,2 @@ -priv/.recovery \ No newline at end of file +priv/.recovery +priv/solc_compilers/ diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 47b30cfa2b..b4f8589a57 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -26,6 +26,7 @@ defmodule Explorer.Application do Supervisor.Spec.worker(SpandexDatadog.ApiServer, [datadog_opts()]), Supervisor.child_spec({Task.Supervisor, name: Explorer.MarketTaskSupervisor}, id: Explorer.MarketTaskSupervisor), Supervisor.child_spec({Task.Supervisor, name: Explorer.TaskSupervisor}, id: Explorer.TaskSupervisor), + Explorer.SmartContract.SolcDownloader, {Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents}, {Admin.Recovery, [[], [name: Admin.Recovery]]}, {TransactionCountCache, [[], []]} diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index beee541ebf..e6fb9c011e 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1143,21 +1143,25 @@ defmodule Explorer.Chain do end @doc """ - Lists the top 250 `t:Explorer.Chain.Address.t/0`'s' in descending order based on coin balance. + Lists the top `t:Explorer.Chain.Address.t/0`'s' in descending order based on coin balance and address hash. """ @spec list_top_addresses :: [{Address.t(), non_neg_integer()}] - def list_top_addresses do - query = + def list_top_addresses(options \\ []) do + paging_options = Keyword.get(options, :paging_options, @default_paging_options) + + base_query = from(a in Address, where: a.fetched_coin_balance > ^0, order_by: [desc: a.fetched_coin_balance, asc: a.hash], preload: [:names], - select: {a, fragment("coalesce(1 + ?, 0)", a.nonce)}, - limit: 250 + select: {a, fragment("coalesce(1 + ?, 0)", a.nonce)} ) - Repo.all(query) + base_query + |> page_addresses(paging_options) + |> limit(^paging_options.page_size) + |> Repo.all() end @doc """ @@ -2293,6 +2297,12 @@ defmodule Explorer.Chain do end) end + defp page_addresses(query, %PagingOptions{key: nil}), do: query + + defp page_addresses(query, %PagingOptions{key: {coin_balance, hash}}) do + where(query, [address], address.fetched_coin_balance <= ^coin_balance and address.hash > ^hash) + end + defp page_blocks(query, %PagingOptions{key: nil}), do: query defp page_blocks(query, %PagingOptions{key: {block_number}}) do diff --git a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex new file mode 100644 index 0000000000..1aa81d2488 --- /dev/null +++ b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex @@ -0,0 +1,92 @@ +defmodule Explorer.SmartContract.SolcDownloader do + @moduledoc """ + Checks to see if the requested solc compiler version exists, and if not it + downloads and stores the file. + """ + use GenServer + + alias Explorer.SmartContract.Solidity.CompilerVersion + + @latest_compiler_refetch_time :timer.minutes(30) + + def ensure_exists(version) do + path = file_path(version) + + if File.exists?(path) do + path + else + {:ok, compiler_versions} = CompilerVersion.fetch_versions() + + if version in compiler_versions do + GenServer.call(__MODULE__, {:ensure_exists, version}, 60_000) + else + false + end + end + end + + def start_link(_) do + GenServer.start_link(__MODULE__, [], name: __MODULE__) + end + + # sobelow_skip ["Traversal"] + @impl true + def init([]) do + File.mkdir(compiler_dir()) + + {:ok, []} + end + + # sobelow_skip ["Traversal"] + @impl true + def handle_call({:ensure_exists, version}, _from, state) do + path = file_path(version) + + if fetch?(version, path) do + temp_path = file_path("#{version}-tmp") + + contents = download(version) + + file = File.open!(temp_path, [:write, :exclusive]) + + IO.binwrite(file, contents) + + File.rename(temp_path, path) + end + + {:reply, path, state} + end + + defp fetch?("latest", path) do + case File.stat(path) do + {:error, :enoent} -> + true + + {:ok, %{mtime: mtime}} -> + last_modified = NaiveDateTime.from_erl!(mtime) + diff = Timex.diff(NaiveDateTime.utc_now(), last_modified, :milliseconds) + + diff > @latest_compiler_refetch_time + end + end + + defp fetch?(_, path) do + not File.exists?(path) + end + + defp file_path(version) do + Path.join(compiler_dir(), "#{version}.js") + end + + defp compiler_dir do + Application.app_dir(:explorer, "priv/solc_compilers/") + end + + defp download(version) do + download_path = "https://ethereum.github.io/solc-bin/bin/soljson-#{version}.js" + + download_path + |> HTTPoison.get!([], timeout: 60_000) + |> Map.get(:body) + end +end diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex index e7f6090707..72c5bae186 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex @@ -3,6 +3,8 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do Module responsible to compile the Solidity code of a given Smart Contract. """ + alias Explorer.SmartContract.SolcDownloader + @new_contract_name "New.sol" @allowed_evm_versions ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"] @@ -79,31 +81,36 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do "byzantium" end - {response, _status} = - System.cmd( - "node", - [ - Application.app_dir(:explorer, "priv/compile_solc.js"), - code, - compiler_version, - optimize_value(optimize), - optimization_runs, - @new_contract_name, - external_libs_string, - checked_evm_version - ] - ) - - with {:ok, contracts} <- Jason.decode(response), - %{"abi" => abi, "evm" => %{"deployedBytecode" => %{"object" => bytecode}}} <- - get_contract_info(contracts, name) do - {:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}} - else - {:error, %Jason.DecodeError{}} -> - {:error, :compilation} - - error -> - parse_error(error) + path = SolcDownloader.ensure_exists(compiler_version) + + if path do + {response, _status} = + System.cmd( + "node", + [ + Application.app_dir(:explorer, "priv/compile_solc.js"), + code, + compiler_version, + optimize_value(optimize), + optimization_runs, + @new_contract_name, + external_libs_string, + checked_evm_version, + path + ] + ) + + with {:ok, contracts} <- Jason.decode(response), + %{"abi" => abi, "evm" => %{"deployedBytecode" => %{"object" => bytecode}}} <- + get_contract_info(contracts, name) do + {:ok, %{"abi" => abi, "bytecode" => bytecode, "name" => name}} + else + {:error, %Jason.DecodeError{}} -> + {:error, :compilation} + + error -> + parse_error(error) + end end end diff --git a/apps/explorer/priv/compile_solc.js b/apps/explorer/priv/compile_solc.js index 7179ebda56..5aaf3f54d7 100755 --- a/apps/explorer/priv/compile_solc.js +++ b/apps/explorer/priv/compile_solc.js @@ -1,7 +1,5 @@ #!/usr/bin/env node -const solc = require('solc'); - var sourceCode = process.argv[2]; var version = process.argv[3]; var optimize = process.argv[4]; @@ -9,38 +7,38 @@ var optimizationRuns = parseInt(process.argv[5], 10); var newContractName = process.argv[6]; var externalLibraries = JSON.parse(process.argv[7]) var evmVersion = process.argv[8]; +var compilerVersionPath = process.argv[9]; + +var solc = require('solc') +var compilerSnapshot = require(compilerVersionPath); +var solc = solc.setupMethods(compilerSnapshot); -var compiled_code = solc.loadRemoteVersion(version, function (err, solcSnapshot) { - if (err) { - console.log(JSON.stringify(err.message)); - } else { - const input = { - language: 'Solidity', - sources: { - [newContractName]: { - content: sourceCode - } - }, - settings: { - evmVersion: evmVersion, - optimizer: { - enabled: optimize == '1', - runs: optimizationRuns - }, - libraries: { - [newContractName]: externalLibraries - }, - outputSelection: { - '*': { - '*': ['*'] - } - } +const input = { + language: 'Solidity', + sources: { + [newContractName]: { + content: sourceCode + } + }, + settings: { + evmVersion: evmVersion, + optimizer: { + enabled: optimize == '1', + runs: optimizationRuns + }, + libraries: { + [newContractName]: externalLibraries + }, + outputSelection: { + '*': { + '*': ['*'] } } - - const output = JSON.parse(solcSnapshot.compile(JSON.stringify(input))) - /** Older solc-bin versions don't use filename as contract key */ - const response = output.contracts[newContractName] || output.contracts[''] - console.log(JSON.stringify(response)); } -}); +} + + +const output = JSON.parse(solc.compile(JSON.stringify(input))) +/** Older solc-bin versions don't use filename as contract key */ +const response = output.contracts[newContractName] || output.contracts[''] +console.log(JSON.stringify(response)); diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 36edace635..a9b479c3bd 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -1415,6 +1415,33 @@ defmodule Explorer.ChainTest do |> Enum.map(fn {address, _transaction_count} -> address end) |> Enum.map(& &1.hash) end + + test "paginates addresses" do + test_hashes = + 4..0 + |> Enum.map(&Explorer.Chain.Hash.cast(Explorer.Chain.Hash.Address, &1)) + |> Enum.map(&elem(&1, 1)) + + result = + 4..1 + |> Enum.map(&insert(:address, fetched_coin_balance: &1, hash: Enum.fetch!(test_hashes, &1 - 1))) + |> Enum.map(& &1.hash) + + options = [paging_options: %PagingOptions{page_size: 1}] + + [{top_address, _}] = Chain.list_top_addresses(options) + assert top_address.hash == List.first(result) + + tail_options = [ + paging_options: %PagingOptions{key: {top_address.fetched_coin_balance.value, top_address.hash}, page_size: 3} + ] + + tail_result = tail_options |> Chain.list_top_addresses() |> Enum.map(fn {address, _} -> address.hash end) + + [_ | expected_tail] = result + + assert tail_result == expected_tail + end end describe "stream_blocks_without_rewards/2" do