diff --git a/apps/block_scout_web/assets/js/lib/market_history_chart.js b/apps/block_scout_web/assets/js/lib/market_history_chart.js index 7cc6527fcf..c863b6aa51 100644 --- a/apps/block_scout_web/assets/js/lib/market_history_chart.js +++ b/apps/block_scout_web/assets/js/lib/market_history_chart.js @@ -75,7 +75,11 @@ function getPriceData (marketHistoryData) { } function getMarketCapData (marketHistoryData, availableSupply) { - return marketHistoryData.map(({ date, closingPrice }) => ({x: date, y: closingPrice * availableSupply})) + if (availableSupply !== null && typeof availableSupply === 'object') { + return marketHistoryData.map(({ date, closingPrice }) => ({x: date, y: closingPrice * availableSupply[date]})) + } else { + return marketHistoryData.map(({ date, closingPrice }) => ({x: date, y: closingPrice * availableSupply})) + } } class MarketHistoryChart { @@ -100,18 +104,25 @@ class MarketHistoryChart { borderColor: sassVariables.secondary, lineTension: 0 } + this.availableSupply = availableSupply config.data.datasets = [this.price, this.marketCap] this.chart = new Chart(el, config) } update (availableSupply, marketHistoryData) { this.price.data = getPriceData(marketHistoryData) - this.marketCap.data = getMarketCapData(marketHistoryData, availableSupply) + if (this.availableSupply !== null && typeof this.availableSupply === 'object') { + const today = new Date().toJSON().slice(0, 10) + this.availableSupply[today] = availableSupply + this.marketCap.data = getMarketCapData(marketHistoryData, this.availableSupply) + } else { + this.marketCap.data = getMarketCapData(marketHistoryData, availableSupply) + } this.chart.update() } } export function createMarketHistoryChart (ctx) { - const availableSupply = ctx.dataset.available_supply + const availableSupply = JSON.parse(ctx.dataset.available_supply) const marketHistoryData = humps.camelizeKeys(JSON.parse(ctx.dataset.market_history_data)) return new MarketHistoryChart(ctx, availableSupply, marketHistoryData) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex index 6aa4cb7470..73bd85915e 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex @@ -40,6 +40,7 @@ defmodule BlockScoutWeb.ChainController do average_block_time: Chain.average_block_time(), blocks: blocks, exchange_rate: exchange_rate, + available_supply: available_supply(Chain.supply_for_days(30), exchange_rate), market_history_data: market_history_data, transaction_estimated_count: transaction_estimated_count, transactions: transactions @@ -78,4 +79,17 @@ defmodule BlockScoutWeb.ChainController do ) ) end + + defp available_supply(:ok, exchange_rate) do + to_string(exchange_rate.available_supply || 0) + end + + defp available_supply({:ok, supply_for_days}, _exchange_rate) do + supply_for_days + |> Jason.encode() + |> case do + {:ok, data} -> data + _ -> [] + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex index ff0c15f50e..c0f4051811 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex @@ -3,7 +3,7 @@
+ <%= gettext("Showing") %> + <%= Cldr.Number.to_string!(@pending_transaction_count, format: "#,###") %> + <%= gettext("Pending Transactions") %> +
-- <%= gettext("Showing") %> - <%= Cldr.Number.to_string!(@pending_transaction_count, format: "#,###") %> - <%= gettext("Pending Transactions") %> -
+ <%= for transaction <- @transactions do %> <%= render BlockScoutWeb.TransactionView, "_tile.html", transaction: transaction %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex index 1021b0b234..42aa38165f 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex @@ -1,48 +1,14 @@+ <%= gettext("Showing") %> + <%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %> + <%= gettext("Validated Transactions") %> +
-- <%= gettext("Showing") %> - <%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %> - <%= gettext("Validated Transactions") %> -
+ <%= for transaction <- @transactions do %> <%= render BlockScoutWeb.TransactionView, "_tile.html", transaction: transaction %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex index ea987e78e1..e1dbbfd7f3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex @@ -1,3 +1,34 @@ defmodule BlockScoutWeb.InternalTransactionView do use BlockScoutWeb, :view + + alias Explorer.Chain.InternalTransaction + + import BlockScoutWeb.Gettext + + @doc """ + Returns the formatted string for the type of the internal transaction. + + When the type is `call`, we return the formatted string for the call type. + + Examples: + + iex> BlockScoutWeb.InternalTransactionView.type(%Explorer.Chain.InternalTransaction{type: :reward}) + "Reward" + + iex> BlockScoutWeb.InternalTransactionView.type(%Explorer.Chain.InternalTransaction{type: :call, call_type: :delegatecall}) + "Delegate Call" + """ + def type(%InternalTransaction{type: :call, call_type: call_type}) do + formatted_type(call_type) + end + + def type(%InternalTransaction{type: type}) do + formatted_type(type) + end + + defp formatted_type(:call), do: gettext("Call") + defp formatted_type(:delegatecall), do: gettext("Delegate Call") + defp formatted_type(:create), do: gettext("Create") + defp formatted_type(:suicide), do: gettext("Suicide") + defp formatted_type(:reward), do: gettext("Reward") end diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 3e010ddf08..d4ebd0a350 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -84,7 +84,7 @@ msgid "A string with the name of the module to be invoked." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:51 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:64 msgid "API" msgstr "" @@ -99,7 +99,7 @@ msgid "API for the %{subnetwork} - BlockScout" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:43 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:56 msgid "Accounts" msgstr "" @@ -144,7 +144,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/block/_link.html.eex:2 -#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:21 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:22 #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:44 msgid "Block #%{number}" msgstr "" @@ -258,8 +258,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_transaction/index.html.eex:14 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:53 -#: lib/block_scout_web/templates/transaction/index.html.eex:53 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:19 +#: lib/block_scout_web/templates/transaction/index.html.eex:19 msgid "Connection Lost, click to load newer transactions" msgstr "" @@ -404,7 +404,7 @@ msgid "Error: (Awaiting internal transactions for reason)" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:15 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:16 #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:19 #: lib/block_scout_web/templates/transaction/_tile.html.eex:26 #: lib/block_scout_web/templates/transaction/overview.html.eex:98 @@ -484,7 +484,7 @@ msgid "Hide" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:31 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:32 #: lib/block_scout_web/templates/transaction/_tile.html.eex:46 msgid "IN" msgstr "" @@ -579,8 +579,8 @@ msgid "More internal transactions have come in" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:48 -#: lib/block_scout_web/templates/transaction/index.html.eex:48 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:14 +#: lib/block_scout_web/templates/transaction/index.html.eex:14 msgid "More transactions have come in" msgstr "" @@ -631,7 +631,7 @@ msgid "Nonce" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:29 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:30 #: lib/block_scout_web/templates/transaction/_tile.html.eex:42 msgid "OUT" msgstr "" @@ -642,9 +642,9 @@ msgstr "" #: lib/block_scout_web/templates/address_validation/index.html.eex:117 #: lib/block_scout_web/templates/block/index.html.eex:20 #: lib/block_scout_web/templates/block_transaction/index.html.eex:50 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:70 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:31 #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:34 -#: lib/block_scout_web/templates/transaction/index.html.eex:70 +#: lib/block_scout_web/templates/transaction/index.html.eex:31 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:24 msgid "Older" msgstr "" @@ -660,12 +660,12 @@ msgid "Owner Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:82 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:95 msgid "POA Core" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:81 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:94 msgid "POA Sokol" msgstr "" @@ -685,11 +685,7 @@ msgid "Parent Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:16 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:27 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:35 -#: lib/block_scout_web/templates/transaction/index.html.eex:16 -#: lib/block_scout_web/templates/transaction/index.html.eex:35 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:44 #: lib/block_scout_web/templates/transaction/overview.html.eex:54 #: lib/block_scout_web/views/transaction_view.ex:57 #: lib/block_scout_web/views/transaction_view.ex:83 @@ -698,7 +694,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_transaction/index.html.eex:60 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:60 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:5 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:9 msgid "Pending Transactions" msgstr "" @@ -760,13 +757,13 @@ msgid "Responses" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:58 -#: lib/block_scout_web/templates/layout/_topnav.html.eex:65 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:71 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:78 msgid "Search" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:58 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:71 msgid "Search by address, transaction hash, or block number" msgstr "" @@ -792,8 +789,8 @@ msgid "Show QR Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:58 -#: lib/block_scout_web/templates/transaction/index.html.eex:58 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:7 +#: lib/block_scout_web/templates/transaction/index.html.eex:7 msgid "Showing" msgstr "" @@ -1004,8 +1001,6 @@ msgstr "" #: lib/block_scout_web/templates/block_transaction/index.html.eex:35 #: lib/block_scout_web/templates/chain/show.html.eex:71 #: lib/block_scout_web/templates/layout/_topnav.html.eex:35 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:56 -#: lib/block_scout_web/templates/transaction/index.html.eex:56 #: lib/block_scout_web/views/address_view.ex:208 msgid "Transactions" msgstr "" @@ -1048,16 +1043,13 @@ msgid "Used" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:9 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:30 -#: lib/block_scout_web/templates/transaction/index.html.eex:9 -#: lib/block_scout_web/templates/transaction/index.html.eex:27 -#: lib/block_scout_web/templates/transaction/index.html.eex:30 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:39 msgid "Validated" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/index.html.eex:60 +#: lib/block_scout_web/templates/transaction/index.html.eex:5 +#: lib/block_scout_web/templates/transaction/index.html.eex:9 msgid "Validated Transactions" msgstr "" @@ -1194,3 +1186,28 @@ msgstr "" #: lib/block_scout_web/views/address_contract_view.ex:15 msgid "true" msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:29 +msgid "Call" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:31 +msgid "Create" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:30 +msgid "Delegate Call" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:33 +msgid "Reward" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:32 +msgid "Suicide" +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 2f34bc42a4..0cfb2d67fc 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 @@ -84,7 +84,7 @@ msgid "A string with the name of the module to be invoked." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:51 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:64 msgid "API" msgstr "" @@ -99,7 +99,7 @@ msgid "API for the %{subnetwork} - BlockScout" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:43 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:56 msgid "Accounts" msgstr "" @@ -144,7 +144,7 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/block/_link.html.eex:2 -#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:21 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:22 #: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:44 msgid "Block #%{number}" msgstr "" @@ -258,8 +258,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_transaction/index.html.eex:14 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:53 -#: lib/block_scout_web/templates/transaction/index.html.eex:53 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:19 +#: lib/block_scout_web/templates/transaction/index.html.eex:19 msgid "Connection Lost, click to load newer transactions" msgstr "" @@ -404,7 +404,7 @@ msgid "Error: (Awaiting internal transactions for reason)" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:15 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:16 #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:19 #: lib/block_scout_web/templates/transaction/_tile.html.eex:26 #: lib/block_scout_web/templates/transaction/overview.html.eex:98 @@ -484,7 +484,7 @@ msgid "Hide" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:31 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:32 #: lib/block_scout_web/templates/transaction/_tile.html.eex:46 msgid "IN" msgstr "" @@ -579,8 +579,8 @@ msgid "More internal transactions have come in" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:48 -#: lib/block_scout_web/templates/transaction/index.html.eex:48 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:14 +#: lib/block_scout_web/templates/transaction/index.html.eex:14 msgid "More transactions have come in" msgstr "" @@ -631,7 +631,7 @@ msgid "Nonce" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:29 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:30 #: lib/block_scout_web/templates/transaction/_tile.html.eex:42 msgid "OUT" msgstr "" @@ -642,9 +642,9 @@ msgstr "" #: lib/block_scout_web/templates/address_validation/index.html.eex:117 #: lib/block_scout_web/templates/block/index.html.eex:20 #: lib/block_scout_web/templates/block_transaction/index.html.eex:50 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:70 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:31 #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:34 -#: lib/block_scout_web/templates/transaction/index.html.eex:70 +#: lib/block_scout_web/templates/transaction/index.html.eex:31 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:24 msgid "Older" msgstr "" @@ -660,12 +660,12 @@ msgid "Owner Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:82 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:95 msgid "POA Core" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:81 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:94 msgid "POA Sokol" msgstr "" @@ -685,11 +685,7 @@ msgid "Parent Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:16 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:27 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:35 -#: lib/block_scout_web/templates/transaction/index.html.eex:16 -#: lib/block_scout_web/templates/transaction/index.html.eex:35 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:44 #: lib/block_scout_web/templates/transaction/overview.html.eex:54 #: lib/block_scout_web/views/transaction_view.ex:57 #: lib/block_scout_web/views/transaction_view.ex:83 @@ -698,7 +694,8 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_transaction/index.html.eex:60 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:60 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:5 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:9 msgid "Pending Transactions" msgstr "" @@ -760,13 +757,13 @@ msgid "Responses" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:58 -#: lib/block_scout_web/templates/layout/_topnav.html.eex:65 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:71 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:78 msgid "Search" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/layout/_topnav.html.eex:58 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:71 msgid "Search by address, transaction hash, or block number" msgstr "" @@ -792,8 +789,8 @@ msgid "Show QR Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:58 -#: lib/block_scout_web/templates/transaction/index.html.eex:58 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:7 +#: lib/block_scout_web/templates/transaction/index.html.eex:7 msgid "Showing" msgstr "" @@ -1004,8 +1001,6 @@ msgstr "" #: lib/block_scout_web/templates/block_transaction/index.html.eex:35 #: lib/block_scout_web/templates/chain/show.html.eex:71 #: lib/block_scout_web/templates/layout/_topnav.html.eex:35 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:56 -#: lib/block_scout_web/templates/transaction/index.html.eex:56 #: lib/block_scout_web/views/address_view.ex:208 msgid "Transactions" msgstr "" @@ -1048,16 +1043,13 @@ msgid "Used" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:9 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:30 -#: lib/block_scout_web/templates/transaction/index.html.eex:9 -#: lib/block_scout_web/templates/transaction/index.html.eex:27 -#: lib/block_scout_web/templates/transaction/index.html.eex:30 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:39 msgid "Validated" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/index.html.eex:60 +#: lib/block_scout_web/templates/transaction/index.html.eex:5 +#: lib/block_scout_web/templates/transaction/index.html.eex:9 msgid "Validated Transactions" msgstr "" @@ -1194,3 +1186,28 @@ msgstr "" #: lib/block_scout_web/views/address_contract_view.ex:15 msgid "true" msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:29 +msgid "Call" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:31 +msgid "Create" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:30 +msgid "Delegate Call" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:33 +msgid "Reward" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/views/internal_transaction_view.ex:32 +msgid "Suicide" +msgstr "" diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/transaction_list_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/transaction_list_page.ex index b3de0908ab..a2915bec1a 100644 --- a/apps/block_scout_web/test/block_scout_web/features/pages/transaction_list_page.ex +++ b/apps/block_scout_web/test/block_scout_web/features/pages/transaction_list_page.ex @@ -11,10 +11,6 @@ defmodule BlockScoutWeb.TransactionListPage do click(session, css("[data-transaction-hash='#{transaction_hash}'] [data-test='transaction_hash_link']")) end - def click_pending(session) do - click(session, css("[data-test='pending_transactions_link']")) - end - def contract_creation(%Transaction{hash: hash}) do css("[data-transaction-hash='#{hash}'] [data-test='transaction_type']", text: "Contract Creation") end @@ -30,4 +26,8 @@ defmodule BlockScoutWeb.TransactionListPage do def visit_page(session) do visit(session, "/txs") end + + def visit_pending_transactions_page(session) do + visit(session, "/pending_transactions") + end end diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs index fc0fe78dee..db14b900d9 100644 --- a/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs @@ -74,10 +74,13 @@ defmodule BlockScoutWeb.ViewingTransactionsTest do |> refute_has(TransactionListPage.transaction(pending)) end - test "viewing the pending tab", %{pending: pending, pending_contract: pending_contract, session: session} do + test "viewing the pending tranasctions list", %{ + pending: pending, + pending_contract: pending_contract, + session: session + } do session - |> TransactionListPage.visit_page() - |> TransactionListPage.click_pending() + |> TransactionListPage.visit_pending_transactions_page() |> assert_has(TransactionListPage.transaction(pending)) |> assert_has(TransactionListPage.transaction(pending_contract)) |> assert_has(TransactionListPage.transaction_status(pending_contract)) diff --git a/apps/block_scout_web/test/block_scout_web/views/internal_transaction_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/internal_transaction_view_test.exs new file mode 100644 index 0000000000..2d7554b7d8 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/internal_transaction_view_test.exs @@ -0,0 +1,40 @@ +defmodule BlockScoutWeb.InternalTransactionViewTest do + use BlockScoutWeb.ConnCase, async: true + + alias BlockScoutWeb.InternalTransactionView + alias Explorer.Chain.InternalTransaction + + doctest BlockScoutWeb.InternalTransactionView + + describe "type/1" do + test "returns the correct string when the type is :call and call type is :call" do + internal_transaction = %InternalTransaction{type: :call, call_type: :call} + + assert InternalTransactionView.type(internal_transaction) == "Call" + end + + test "returns the correct string when the type is :call and call type is :delegate_call" do + internal_transaction = %InternalTransaction{type: :call, call_type: :delegatecall} + + assert InternalTransactionView.type(internal_transaction) == "Delegate Call" + end + + test "returns the correct string when the type is :create" do + internal_transaction = %InternalTransaction{type: :create} + + assert InternalTransactionView.type(internal_transaction) == "Create" + end + + test "returns the correct string when the type is :suicide" do + internal_transaction = %InternalTransaction{type: :suicide} + + assert InternalTransactionView.type(internal_transaction) == "Suicide" + end + + test "returns the correct string when the type is :reward" do + internal_transaction = %InternalTransaction{type: :reward} + + assert InternalTransactionView.type(internal_transaction) == "Reward" + end + end +end diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 87f0c2e1b4..db41d70124 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -28,6 +28,14 @@ config :explorer, Explorer.Counters.TokenTransferCounter, enabled: true config :explorer, Explorer.Counters.TokenHoldersCounter, enabled: true, enable_consolidation: true +if System.get_env("SUPPLY_MODULE") == "TransactionAndLog" do + config :explorer, supply: Explorer.Chain.Supply.TransactionAndLog +end + +if System.get_env("SOURCE_MODULE") == "TransactionAndLog" do + config :explorer, Explorer.ExchangeRates, source: Explorer.ExchangeRates.Source.TransactionAndLog +end + config :explorer, solc_bin_api_url: "https://solc-bin.ethereum.org" diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs index 7f6d19ae82..9626facf30 100644 --- a/apps/explorer/config/test.exs +++ b/apps/explorer/config/test.exs @@ -27,6 +27,9 @@ if File.exists?(file = "test.secret.exs") do import_config file end +config :explorer, Explorer.ExchangeRates.Source.TransactionAndLog, + secondary_source: Explorer.ExchangeRates.Source.OneCoinSource + variant = if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do "parity" diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 52c86d89e2..9538a862c5 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1929,6 +1929,11 @@ defmodule Explorer.Chain do Application.get_env(:explorer, :supply, Explorer.Chain.Supply.ProofOfAuthority) end + @doc """ + Calls supply_for_days from the configured supply_module + """ + def supply_for_days(days_count), do: supply_module().supply_for_days(days_count) + @doc """ Streams a lists token contract addresses that haven't been cataloged. """ diff --git a/apps/explorer/lib/explorer/chain/supply.ex b/apps/explorer/lib/explorer/chain/supply.ex index 7d365f2784..b442e40125 100644 --- a/apps/explorer/lib/explorer/chain/supply.ex +++ b/apps/explorer/lib/explorer/chain/supply.ex @@ -9,10 +9,24 @@ defmodule Explorer.Chain.Supply do @doc """ The current total number of coins minted minus verifiably burned coins. """ - @callback total :: non_neg_integer() + @callback total :: non_neg_integer() | %Decimal{sign: 1} @doc """ The current number coins in the market for trading. """ - @callback circulating :: non_neg_integer() + @callback circulating :: non_neg_integer() | %Decimal{sign: 1} + + @doc """ + A map of total supplies per day, optional. + """ + @callback supply_for_days(days_count :: integer) :: {:ok, term} | {:error, term} | :ok + + defmacro __using__(_opts) do + quote do + @behaviour Explorer.Chain.Supply + def supply_for_days(_days_count), do: :ok + + defoverridable supply_for_days: 1 + end + end end diff --git a/apps/explorer/lib/explorer/chain/supply/proof_of_authority.ex b/apps/explorer/lib/explorer/chain/supply/proof_of_authority.ex index 6d8824849c..f9923eeca3 100644 --- a/apps/explorer/lib/explorer/chain/supply/proof_of_authority.ex +++ b/apps/explorer/lib/explorer/chain/supply/proof_of_authority.ex @@ -12,11 +12,9 @@ defmodule Explorer.Chain.Supply.ProofOfAuthority do See https://github.com/poanetwork/wiki/wiki/POA-Token-Supply for more information. """ + use Explorer.Chain.Supply alias Explorer.Chain - alias Explorer.Chain.Supply - - @behaviour Supply @initial_supply 252_460_800 @reserved_for_vesting 50_492_160 @@ -31,12 +29,10 @@ defmodule Explorer.Chain.Supply.ProofOfAuthority do ~D[2019-12-15] => 0.125 } - @impl Supply def circulating do total() - reserved_supply(Date.utc_today()) end - @impl Supply def total do initial_supply = initial_supply() block_height = block_height() diff --git a/apps/explorer/lib/explorer/chain/supply/transaction_and_log.ex b/apps/explorer/lib/explorer/chain/supply/transaction_and_log.ex new file mode 100644 index 0000000000..03e63ad91e --- /dev/null +++ b/apps/explorer/lib/explorer/chain/supply/transaction_and_log.ex @@ -0,0 +1,78 @@ +defmodule Explorer.Chain.Supply.TransactionAndLog do + @moduledoc """ + Defines the supply API for calculating the supply for smaller chains with + specific mint and burn events + """ + use Explorer.Chain.Supply + + alias Explorer.Chain.{InternalTransaction, Log, Wei} + alias Explorer.{Repo, Chain} + + {:ok, base_wei} = Wei.cast(0) + @base_wei base_wei + + {:ok, burn_address} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + + @burn_address burn_address + @bridge_edge "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a" + + import Ecto.Query, only: [from: 2] + + def circulating, do: total(Timex.now()) + + def total, do: total(Timex.now()) + + @doc false + @spec total(DateTime.t()) :: %Decimal{sign: 1} + def total(on_date) do + on_date + |> minted_value + |> Wei.sub(burned_value(on_date)) + |> Wei.to(:ether) + end + + def supply_for_days(days_count) when is_integer(days_count) and days_count > 0 do + past_days = -(days_count - 1) + + result = + for i <- past_days..0, into: %{} do + datetime = Timex.shift(Timex.now(), days: i) + {DateTime.to_date(datetime), total(datetime)} + end + + {:ok, result} + end + + defp minted_value(on_date) do + query = + from( + l in Log, + join: t in assoc(l, :transaction), + join: b in assoc(t, :block), + where: b.timestamp <= ^on_date and l.first_topic == @bridge_edge, + select: fragment("concat('0x', encode(?, 'hex'))", l.data) + ) + + query + |> Repo.all() + |> Enum.reduce(@base_wei, fn data, acc -> + {:ok, wei_value} = Wei.cast(data) + Wei.sum(wei_value, acc) + end) + end + + defp burned_value(on_date) do + query = + from( + it in InternalTransaction, + join: t in assoc(it, :transaction), + join: b in assoc(t, :block), + where: b.timestamp <= ^on_date and it.to_address_hash == ^@burn_address, + select: it.value + ) + + query + |> Repo.all() + |> Enum.reduce(@base_wei, fn data, acc -> Wei.sum(data, acc) end) + end +end diff --git a/apps/explorer/lib/explorer/chain/wei.ex b/apps/explorer/lib/explorer/chain/wei.ex index 33f76a91a8..bd1c42c09c 100644 --- a/apps/explorer/lib/explorer/chain/wei.ex +++ b/apps/explorer/lib/explorer/chain/wei.ex @@ -130,6 +130,22 @@ defmodule Explorer.Chain.Wei do |> from(:wei) end + @doc """ + Subtracts two Wei values. + + ## Example + + iex> first = %Explorer.Chain.Wei{value: Decimal.new(1_123)} + iex> second = %Explorer.Chain.Wei{value: Decimal.new(1_000)} + iex> Explorer.Chain.Wei.sub(first, second) + %Explorer.Chain.Wei{value: Decimal.new(123)} + """ + def sub(%Wei{value: wei_1}, %Wei{value: wei_2}) do + wei_1 + |> Decimal.sub(wei_2) + |> from(:wei) + end + @doc """ Converts `Decimal` representations of various wei denominations (wei, Gwei, ether) to a wei base unit. diff --git a/apps/explorer/lib/explorer/exchange_rates/source/transaction_and_log.ex b/apps/explorer/lib/explorer/exchange_rates/source/transaction_and_log.ex new file mode 100644 index 0000000000..3dd11f0355 --- /dev/null +++ b/apps/explorer/lib/explorer/exchange_rates/source/transaction_and_log.ex @@ -0,0 +1,50 @@ +defmodule Explorer.ExchangeRates.Source.TransactionAndLog do + @moduledoc """ + Adapter for calculating the market cap and total supply from logs and transactions + while still getting other info like price in dollars and bitcoin from a secondary source + """ + + alias Explorer.ExchangeRates.{Source, Token} + alias Explorer.Chain + + @behaviour Source + + @impl Source + def fetch_exchange_rates do + token_data = + secondary_source().fetch_exchange_rates() + |> elem(1) + |> Enum.find(fn token -> token.symbol == Explorer.coin() end) + |> build_struct + + {:ok, [token_data]} + end + + defp build_struct(original_token) do + %Token{ + available_supply: to_decimal(Chain.circulating_supply()), + btc_value: original_token.btc_value, + id: original_token.id, + last_updated: original_token.last_updated, + market_cap_usd: Decimal.mult(to_decimal(Chain.circulating_supply()), original_token.usd_value), + name: original_token.name, + symbol: original_token.symbol, + usd_value: original_token.usd_value, + volume_24h_usd: original_token.volume_24h_usd + } + end + + defp to_decimal(value) do + Decimal.new(value) + end + + @spec secondary_source() :: module() + defp secondary_source do + config(:secondary_source) || Explorer.ExchangeRates.Source.CoinMarketCap + end + + @spec config(atom()) :: term + defp config(key) do + Application.get_env(:explorer, __MODULE__, [])[key] + end +end diff --git a/apps/explorer/test/explorer/chain/supply/transaction_and_log_test.exs b/apps/explorer/test/explorer/chain/supply/transaction_and_log_test.exs new file mode 100644 index 0000000000..5126d7acaf --- /dev/null +++ b/apps/explorer/test/explorer/chain/supply/transaction_and_log_test.exs @@ -0,0 +1,122 @@ +defmodule Explorer.Chain.Supply.TransactionAndLogTest do + use Explorer.DataCase + alias Explorer.Chain + alias Explorer.Chain.Supply.TransactionAndLog + + setup do + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + + burn_address = + case Chain.hash_to_address(burn_address_hash) do + {:ok, burn_address} -> burn_address + {:error, :not_found} -> insert(:address, hash: "0x0000000000000000000000000000000000000000") + end + + {:ok, %{burn_address: burn_address}} + end + + describe "total/1" do + test "today with no mints or burns brings zero" do + assert TransactionAndLog.total(Timex.now()) == Decimal.new(0) + end + + test "today with mints and burns calculates a value", %{burn_address: burn_address} do + old_block = insert(:block, timestamp: Timex.shift(Timex.now(), days: -1), number: 1000) + + insert(:log, + transaction: + insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 2), + first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", + data: "0x0000000000000000000000000000000000000000000000008ac7230489e80000" + ) + + insert(:internal_transaction, + index: 527, + transaction: + insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 3), + to_address: burn_address, + value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" + ) + + assert TransactionAndLog.total(Timex.now()) == Decimal.new(9) + end + + test "yesterday with mints and burns calculates a value ignoring whatever happened today", %{ + burn_address: burn_address + } do + old_block = insert(:block, timestamp: Timex.shift(Timex.now(), days: -1), number: 1000) + + insert(:log, + transaction: + insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 2), + first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", + data: "0x0000000000000000000000000000000000000000000000008ac7230489e80000" + ) + + new_block = insert(:block, timestamp: Timex.now(), number: 1001) + + insert(:internal_transaction, + index: 527, + transaction: + insert(:transaction, block: new_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 3), + to_address: burn_address, + value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" + ) + + assert TransactionAndLog.total(Timex.shift(Timex.now(), days: -1)) == Decimal.new(10) + end + end + + describe "total/0" do + test "calculates the same value as total/1 receiving today's date", %{burn_address: burn_address} do + old_block = insert(:block, timestamp: Timex.shift(Timex.now(), days: -1), number: 1000) + + insert(:log, + transaction: + insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 2), + first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", + data: "0x0000000000000000000000000000000000000000000000008ac7230489e80000" + ) + + insert(:internal_transaction, + index: 527, + transaction: + insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 3), + to_address: burn_address, + value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" + ) + + assert TransactionAndLog.total() == TransactionAndLog.total(Timex.now()) + end + end + + describe "supply_for_days/1" do + test "bring the supply of today and yesterday when receiving 2", %{burn_address: burn_address} do + old_block = insert(:block, timestamp: Timex.shift(Timex.now(), days: -1), number: 1000) + + insert(:log, + transaction: + insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 2), + first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", + data: "0x0000000000000000000000000000000000000000000000008ac7230489e80000" + ) + + new_block = insert(:block, timestamp: Timex.now(), number: 1001) + + insert(:internal_transaction, + index: 527, + transaction: + insert(:transaction, block: new_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 3), + to_address: burn_address, + value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" + ) + + expected_result = %{ + Timex.shift(Timex.today(), days: -1) => Decimal.new(10), + Timex.today() => Decimal.new(9) + } + + assert TransactionAndLog.supply_for_days(2) == {:ok, expected_result} + end + end +end diff --git a/apps/explorer/test/explorer/chain/wei_test.exs b/apps/explorer/test/explorer/chain/wei_test.exs index 003d85e97e..782d736928 100644 --- a/apps/explorer/test/explorer/chain/wei_test.exs +++ b/apps/explorer/test/explorer/chain/wei_test.exs @@ -54,4 +54,50 @@ defmodule Explorer.Chain.WeiTest do test "type/0" do assert Wei.type() == :decimal end + + describe "sum/1" do + test "with two positive values return the sum of them" do + first = %Explorer.Chain.Wei{value: Decimal.new(123)} + second = %Explorer.Chain.Wei{value: Decimal.new(1_000)} + + assert Explorer.Chain.Wei.sum(first, second) == %Explorer.Chain.Wei{value: Decimal.new(1_123)} + end + + test "with a positive and a negative value return the positive minus the negative's absolute" do + first = %Explorer.Chain.Wei{value: Decimal.new(123)} + second = %Explorer.Chain.Wei{value: Decimal.new(-100)} + + assert Explorer.Chain.Wei.sum(first, second) == %Explorer.Chain.Wei{value: Decimal.new(23)} + end + end + + describe "sub/1" do + test "with a negative second parameter return the sum of the absolute values" do + first = %Explorer.Chain.Wei{value: Decimal.new(123)} + second = %Explorer.Chain.Wei{value: Decimal.new(-100)} + + assert Explorer.Chain.Wei.sub(first, second) == %Explorer.Chain.Wei{value: Decimal.new(223)} + end + + test "with a negative first parameter return the negative of the sum of the absolute values" do + first = %Explorer.Chain.Wei{value: Decimal.new(-123)} + second = %Explorer.Chain.Wei{value: Decimal.new(100)} + + assert Explorer.Chain.Wei.sub(first, second) == %Explorer.Chain.Wei{value: Decimal.new(-223)} + end + + test "with a larger first parameter return a positive number" do + first = %Explorer.Chain.Wei{value: Decimal.new(123)} + second = %Explorer.Chain.Wei{value: Decimal.new(100)} + + assert Explorer.Chain.Wei.sub(first, second) == %Explorer.Chain.Wei{value: Decimal.new(23)} + end + + test "with a larger second parameter return a negative number" do + first = %Explorer.Chain.Wei{value: Decimal.new(23)} + second = %Explorer.Chain.Wei{value: Decimal.new(100)} + + assert Explorer.Chain.Wei.sub(first, second) == %Explorer.Chain.Wei{value: Decimal.new(-77)} + end + end end diff --git a/apps/explorer/test/explorer/exchange_rates/source/transaction_and_log_test.exs b/apps/explorer/test/explorer/exchange_rates/source/transaction_and_log_test.exs new file mode 100644 index 0000000000..f5821ed2f4 --- /dev/null +++ b/apps/explorer/test/explorer/exchange_rates/source/transaction_and_log_test.exs @@ -0,0 +1,11 @@ +defmodule Explorer.ExchangeRates.Source.TransactionAndLogTest do + use Explorer.DataCase + alias Explorer.ExchangeRates.Source.TransactionAndLog + alias Explorer.ExchangeRates.Token + + describe "fetch_exchange_rates/1" do + test "bring a list with one %Token{}" do + assert {:ok, [%Token{}]} = TransactionAndLog.fetch_exchange_rates() + end + end +end diff --git a/apps/explorer/test/support/fakes/one_coin_source.ex b/apps/explorer/test/support/fakes/one_coin_source.ex new file mode 100644 index 0000000000..fc9781abe0 --- /dev/null +++ b/apps/explorer/test/support/fakes/one_coin_source.ex @@ -0,0 +1,25 @@ +defmodule Explorer.ExchangeRates.Source.OneCoinSource do + @moduledoc false + + alias Explorer.ExchangeRates.Source + alias Explorer.ExchangeRates.Token + + @behaviour Source + + @impl Source + def fetch_exchange_rates do + pseudo_token = %Token{ + available_supply: Decimal.new(10_000_000), + btc_value: Decimal.new(1), + id: "", + last_updated: Timex.now(), + name: "", + market_cap_usd: Decimal.new(10_000_000), + symbol: Explorer.coin(), + usd_value: Decimal.new(1), + volume_24h_usd: Decimal.new(1) + } + + {:ok, [pseudo_token]} + end +end