Merge branch 'master' into 638

pull/661/head
Andrew Cravenho 6 years ago committed by GitHub
commit 5723fe512b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .gitignore
  2. 6
      apps/block_scout_web/assets/__tests__/lib/currency.js
  3. 2
      apps/block_scout_web/assets/__tests__/pages/chain.js
  4. 2
      apps/block_scout_web/assets/js/app.js
  5. 38
      apps/block_scout_web/assets/js/lib/currency.js
  6. 23
      apps/block_scout_web/assets/js/lib/market_history_chart.js
  7. 3
      apps/block_scout_web/assets/js/lib/stop_propagation.js
  8. 10
      apps/block_scout_web/assets/js/pages/chain.js
  9. 5
      apps/block_scout_web/assets/package-lock.json
  10. 1
      apps/block_scout_web/assets/package.json
  11. 42
      apps/block_scout_web/lib/block_scout_web/exchange_rates/usd.ex
  12. 7
      apps/block_scout_web/lib/block_scout_web/templates/address/_balance_card.html.eex
  13. 20
      apps/block_scout_web/lib/block_scout_web/templates/address/_token_holdings.html.eex
  14. 2
      apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex
  15. 2
      apps/block_scout_web/lib/block_scout_web/templates/api_docs/_action_tile.html.eex
  16. 6
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  17. 7
      apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex
  18. 6
      apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
  19. 19
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  20. 15
      apps/block_scout_web/lib/block_scout_web/views/chain_view.ex
  21. 38
      apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex
  22. 35
      apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
  23. 75
      apps/block_scout_web/priv/gettext/default.pot
  24. 75
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  25. 48
      apps/block_scout_web/test/block_scout_web/exchange_rates/usd_test.exs
  26. 5
      apps/block_scout_web/test/block_scout_web/features/pages/chain_page.ex
  27. 21
      apps/block_scout_web/test/block_scout_web/views/address_view_test.exs
  28. 19
      apps/block_scout_web/test/block_scout_web/views/chain_view_test.exs
  29. 9
      apps/block_scout_web/test/block_scout_web/views/currency_helpers_test.exs
  30. 10
      apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs
  31. 25
      apps/indexer/lib/indexer/token_balances.ex
  32. 36
      apps/indexer/test/indexer/token_balances_test.exs
  33. 2
      coveralls.json

5
.gitignore vendored

@ -1,6 +1,7 @@
# App artifacts
/_build
/apps/*/cover
/apps/*/logs
/cover
/db
/deps
@ -15,8 +16,7 @@ erl_crash.dump
npm-debug.log
# Static artifacts
/apps/block_scout_web/assets/node_modules
/apps/explorer/node_modules
/apps/**/node_modules
# Since we are building assets from assets/,
# we ignore priv/static. You may want to comment
@ -30,7 +30,6 @@ npm-debug.log
# secrets files as long as you replace their contents by environment
# variables.
/apps/*/config/*.secret.exs
/apps/*/cover/
# Wallaby screenshots
screenshots/

@ -1,7 +1,11 @@
import { formatUsdValue } from '../../js/lib/currency'
test('formatUsdValue', () => {
expect(formatUsdValue(0.0000001)).toEqual('< $0.000001 USD')
window.localized = {
'Less than': 'Less than'
}
expect(formatUsdValue(0)).toEqual('$0.000000 USD')
expect(formatUsdValue(0.0000001)).toEqual('Less than $0.000001 USD')
expect(formatUsdValue(0.123456789)).toEqual('$0.123457 USD')
expect(formatUsdValue(0.1234)).toEqual('$0.123400 USD')
expect(formatUsdValue(1.23456789)).toEqual('$1.23 USD')

@ -40,7 +40,6 @@ test('RECEIVED_NEW_EXCHANGE_RATE', () => {
msg: {
exchangeRate: {
availableSupply: 1000000,
usdValue: 1.23,
marketCapUsd: 1230000
},
marketHistoryData: { data: 'some stuff' }
@ -50,7 +49,6 @@ test('RECEIVED_NEW_EXCHANGE_RATE', () => {
expect(output.availableSupply).toEqual(1000000)
expect(output.marketHistoryData).toEqual({ data: 'some stuff' })
expect(output.usdExchangeRate).toEqual(1.23)
expect(output.usdMarketCap).toEqual(1230000)
})

@ -21,6 +21,7 @@ import 'bootstrap'
import './locale'
import './lib/clipboard_buttons'
import './lib/currency'
import './lib/from_now'
import './lib/loading_element'
import './lib/market_history_chart'
@ -31,6 +32,7 @@ import './lib/pretty_json'
import './lib/try_api'
import './lib/token_balance_dropdown'
import './lib/token_transfers_toggle'
import './lib/stop_propagation'
import './pages/address'
import './pages/block'

@ -1,8 +1,44 @@
import $ from 'jquery'
import humps from 'humps'
import numeral from 'numeral'
import { BigNumber } from 'bignumber.js'
import socket from '../socket'
export function formatUsdValue (value) {
if (value < 0.000001) return '< $0.000001 USD'
if (value === 0) return '$0.000000 USD'
if (value < 0.000001) return `${window.localized['Less than']} $0.000001 USD`
if (value < 1) return `$${numeral(value).format('0.000000')} USD`
if (value < 100000) return `$${numeral(value).format('0,0.00')} USD`
return `$${numeral(value).format('0,0')} USD`
}
function weiToEther (wei) {
return new BigNumber(wei).dividedBy('1000000000000000000').toNumber()
}
function etherToUSD (ether, usdExchangeRate) {
return new BigNumber(ether).multipliedBy(usdExchangeRate).toNumber()
}
function formatAllUsdValues () {
$('[data-usd-value]').each((i, el) => {
el.innerHTML = formatUsdValue(el.dataset.usdValue)
})
}
formatAllUsdValues()
function tryUpdateCalculatedUsdValues (el, usdExchangeRate = el.dataset.usdExchangeRate) {
if (!el.dataset.hasOwnProperty('weiValue')) return
const ether = weiToEther(el.dataset.weiValue)
const usd = etherToUSD(ether, usdExchangeRate)
const formattedUsd = formatUsdValue(usd)
if (formattedUsd !== el.innerHTML) el.innerHTML = formattedUsd
}
function updateAllCalculatedUsdValues (usdExchangeRate) {
$('[data-usd-exchange-rate]').each((i, el) => tryUpdateCalculatedUsdValues(el, usdExchangeRate))
}
updateAllCalculatedUsdValues()
export const exchangeRateChannel = socket.channel(`exchange_rate:new_rate`)
exchangeRateChannel.join()
exchangeRateChannel.on('new_rate', (msg) => updateAllCalculatedUsdValues(humps.camelizeKeys(msg).exchangeRate.usdValue))

@ -1,6 +1,7 @@
import Chart from 'chart.js'
import humps from 'humps'
import numeral from 'numeral'
import { formatUsdValue } from '../lib/currency'
import sassVariables from '../../css/app.scss'
const config = {
@ -33,7 +34,7 @@ const config = {
},
ticks: {
beginAtZero: true,
callback: (value, index, values) => formatPrice(value),
callback: (value, index, values) => `$${numeral(value).format('0,0.00')}`,
maxTicksLimit: 4
}
}, {
@ -56,10 +57,10 @@ const config = {
callbacks: {
label: ({datasetIndex, yLabel}, {datasets}) => {
const label = datasets[datasetIndex].label
if (datasets[datasetIndex].label === 'Price') {
return `${label}: ${formatPrice(yLabel)}`
} else if (datasets[datasetIndex].label === 'Market Cap') {
return `${label}: ${formatMarketCap(yLabel)}`
if (datasets[datasetIndex].yAxisID === 'price') {
return `${label}: ${formatUsdValue(yLabel)}`
} else if (datasets[datasetIndex].yAxisID === 'marketCap') {
return `${label}: ${formatUsdValue(yLabel)}`
} else {
return yLabel
}
@ -69,14 +70,6 @@ const config = {
}
}
function formatPrice (price) {
return `$${numeral(price).format('0,0.00[0000000000000000]')}`
}
function formatMarketCap (marketCap) {
return numeral(marketCap).format('($0,0a)')
}
function getPriceData (marketHistoryData) {
return marketHistoryData.map(({ date, closingPrice }) => ({x: date, y: closingPrice}))
}
@ -88,7 +81,7 @@ function getMarketCapData (marketHistoryData, availableSupply) {
class MarketHistoryChart {
constructor (el, availableSupply, marketHistoryData) {
this.price = {
label: 'Price',
label: window.localized['Price'],
yAxisID: 'price',
data: getPriceData(marketHistoryData),
fill: false,
@ -98,7 +91,7 @@ class MarketHistoryChart {
lineTension: 0
}
this.marketCap = {
label: 'Market Cap',
label: window.localized['Market Cap'],
yAxisID: 'marketCap',
data: getMarketCapData(marketHistoryData, availableSupply),
fill: false,

@ -0,0 +1,3 @@
import $ from 'jquery'
$('[data-selector="stop-propagation"]').click((event) => event.stopPropagation())

@ -4,7 +4,7 @@ import numeral from 'numeral'
import router from '../router'
import socket from '../socket'
import { updateAllAges } from '../lib/from_now'
import { formatUsdValue } from '../lib/currency'
import { exchangeRateChannel, formatUsdValue } from '../lib/currency'
import { batchChannel, initRedux } from '../utils'
import { createMarketHistoryChart } from '../lib/market_history_chart'
@ -19,7 +19,6 @@ export const initialState = {
newBlock: null,
newTransactions: [],
transactionCount: null,
usdExchangeRate: null,
usdMarketCap: null
}
@ -45,7 +44,6 @@ export function reducer (state = initialState, action) {
return Object.assign({}, state, {
availableSupply: action.msg.exchangeRate.availableSupply,
marketHistoryData: action.msg.marketHistoryData,
usdExchangeRate: action.msg.exchangeRate.usdValue,
usdMarketCap: action.msg.exchangeRate.marketCapUsd
})
}
@ -85,8 +83,6 @@ router.when('', { exactPathMatch: true }).then(() => initRedux(reducer, {
blocksChannel.join()
blocksChannel.on('new_block', msg => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) }))
const exchangeRateChannel = socket.channel(`exchange_rate:new_rate`)
exchangeRateChannel.join()
exchangeRateChannel.on('new_rate', (msg) => store.dispatch({ type: 'RECEIVED_NEW_EXCHANGE_RATE', msg: humps.camelizeKeys(msg) }))
const transactionsChannel = socket.channel(`transactions:new_transaction`)
@ -103,7 +99,6 @@ router.when('', { exactPathMatch: true }).then(() => initRedux(reducer, {
const $blockList = $('[data-selector="chain-block-list"]')
const $channelBatching = $('[data-selector="channel-batching-message"]')
const $channelBatchingCount = $('[data-selector="channel-batching-count"]')
const $exchangeRate = $('[data-selector="exchange-rate"]')
const $marketCap = $('[data-selector="market-cap"]')
const $transactionsList = $('[data-selector="transactions-list"]')
const $transactionCount = $('[data-selector="transaction-count"]')
@ -114,9 +109,6 @@ router.when('', { exactPathMatch: true }).then(() => initRedux(reducer, {
if (oldState.averageBlockTime !== state.averageBlockTime) {
$averageBlockTime.empty().append(state.averageBlockTime)
}
if (oldState.usdExchangeRate !== state.usdExchangeRate) {
$exchangeRate.empty().append(formatUsdValue(state.usdExchangeRate))
}
if (oldState.usdMarketCap !== state.usdMarketCap) {
$marketCap.empty().append(formatUsdValue(state.usdMarketCap))
}

@ -1448,6 +1448,11 @@
"integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
"dev": true
},
"bignumber.js": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
"integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ=="
},
"binary-extensions": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",

@ -20,6 +20,7 @@
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.1.0-4",
"bignumber.js": "^7.2.1",
"bootstrap": "^4.1.0",
"chart.js": "^2.7.2",
"clipboard": "^2.0.1",

@ -1,42 +0,0 @@
defmodule BlockScoutWeb.ExchangeRates.USD do
@moduledoc """
Struct and associated conversion functions for USD currency
"""
@typedoc """
Represents USD currency
* `:value` - value in USD
"""
@type t :: %__MODULE__{
value: Decimal.t() | nil
}
defstruct ~w(value)a
alias Explorer.Chain.Wei
alias Explorer.ExchangeRates.Token
def from(nil), do: null()
def from(%Decimal{} = usd_decimal) do
%__MODULE__{value: usd_decimal}
end
def from(nil, _), do: null()
def from(_, nil), do: null()
def from(%Wei{value: nil}, _), do: null()
def from(_, %Token{usd_value: nil}), do: null()
def from(%Wei{} = wei, %Token{usd_value: exchange_rate}) do
ether = Wei.to(wei, :ether)
%__MODULE__{value: Decimal.mult(ether, exchange_rate)}
end
def null do
%__MODULE__{value: nil}
end
end

@ -4,9 +4,12 @@
<span></span>
<div class="text-right">
<h3 class="text-white" data-test="address_balance"><%= balance(@address) %></h3>
<span class="text-light"><%= formatted_usd(@address, @exchange_rate) %></span>
<span class="text-light"
data-wei-value="<%= if @address.fetched_coin_balance, do: @address.fetched_coin_balance.value %>"
data-usd-exchange-rate="<%= @exchange_rate.usd_value %>">
</span>
<div class="mt-3" data-token-balance-dropdown data-api_path=<%= address_token_balance_path(@conn, :index, :en, @address.hash) %> >
<div class="mt-3" data-token-balance-dropdown data-api_path="<%= address_token_balance_path(@conn, :index, :en, @address.hash) %>">
<p data-loading class="mb-0 text-light" style="display: none;">
<i class="fa fa-spinner fa-spin"></i>
<%= gettext("Fetching tokens...") %>

@ -1,20 +0,0 @@
<div class="card">
<div class="card-body">
<h2 class="card-title"><%= gettext "Token Holdings" %></h2>
<!-- Dropdown -->
<div data-token-balance-dropdown
data-api_path=<%= address_token_balance_path(@conn, :index, :en, @address.hash) %>
class="icon-links ml-3 mb-3"
>
<p data-loading class="mb-0" stytle="display: none">
<i class="fa fa-spinner fa-spin"></i>
<%= gettext("Fetching tokens...") %>
</p>
<p data-error-message class="mb-0" style="display: none">
<%= gettext("Error tryng to fetch balances.") %>
</p>
</div>
</div>
</div>

@ -51,7 +51,7 @@
</div>
</div>
</div>
<div class="card-section col-md-6 col-lg-4" data-selector="balance-card">
<div class="card-section col-md-12 col-lg-4" data-selector="balance-card">
<%= render BlockScoutWeb.AddressView, "_balance_card.html", conn: @conn, address: @address, exchange_rate: @exchange_rate %>
</div>

@ -7,7 +7,7 @@
<span class="badge badge-primary tile-badge float-right mr-1"><%= gettext "GET" %></span>
<strong class="tile-label"><%= @action.name %></strong>
</h3>
<h4 class="text-dark"><%= raw @action.description %></h4>
<h4 class="text-dark"><span data-selector="stop-propagation"><%= raw @action.description %></span></h4>
<code><%= raw query_params(@module_name, @action) %></code>
</button>
</div>

@ -9,16 +9,14 @@
<span class="dashboard-banner-chart-legend-label">
<%= gettext "Price" %>
</span>
<span class="dashboard-banner-chart-legend-value" data-selector="exchange-rate">
<%= format_exchange_rate(@exchange_rate) %>
<span class="dashboard-banner-chart-legend-value" data-selector="exchange-rate" data-wei-value="<%= Explorer.Chain.Wei.from(Decimal.new(1), :ether).value %>" data-usd-exchange-rate="<%= @exchange_rate.usd_value %>">
</span>
</div>
<div class="dashboard-banner-chart-legend-item">
<span class="dashboard-banner-chart-legend-label">
<%= gettext "Market Cap" %>
</span>
<span class="dashboard-banner-chart-legend-value" data-selector="market-cap">
<%= format_market_cap(@exchange_rate) %>
<span class="dashboard-banner-chart-legend-value" data-selector="market-cap" data-usd-value="<%= @exchange_rate.market_cap_usd %>">
</span>
</div>
</div>

@ -18,6 +18,13 @@
</main>
<%= render BlockScoutWeb.LayoutView, "_footer.html", assigns %>
</div>
<script>
window.localized = {
'Less than': '<%= gettext("Less than") %>',
'Market Cap': '<%= gettext("Market Cap") %>',
'Price': '<%= gettext("Price") %>',
}
</script>
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>

@ -55,7 +55,9 @@
<!-- TX Fee -->
<dl class="row">
<dt class="col-sm-3 text-muted"> <%= gettext "TX Fee" %> </dt>
<dd class="col-sm-9"> <%= formatted_fee(@transaction, denomination: :ether) %> (<%= formatted_fee(@transaction, exchange_rate: @exchange_rate) %>)</dd>
<dd class="col-sm-9">
<%= formatted_fee(@transaction, denomination: :ether) %> (<span data-wei-value=<%= fee(@transaction) %> data-usd-exchange-rate=<%= @exchange_rate.usd_value %>></span>)
</dd>
</dl>
<!-- Input -->
@ -82,7 +84,7 @@
<h2 class="card-title text-white"><%= gettext "Ether" %> <%= gettext "Value" %></h2>
<div class="text-right">
<h3 class="text-white"> <%= value(@transaction) %></h3>
<span class="text-light"> <%= formatted_usd_value(@transaction, @exchange_rate) %></span>
<span class="text-light" data-wei-value=<%= @transaction.value.value %> data-usd-exchange-rate=<%= @exchange_rate.usd_value %>></span>
</div>
</div>
</div>

@ -1,10 +1,7 @@
defmodule BlockScoutWeb.AddressView do
use BlockScoutWeb, :view
alias Explorer.Chain.{Address, Hash, SmartContract, Wei}
alias Explorer.ExchangeRates.Token
alias BlockScoutWeb.ExchangeRates.USD
alias Explorer.Chain.{Address, Hash, SmartContract}
@dialyzer :no_match
@ -37,20 +34,6 @@ defmodule BlockScoutWeb.AddressView do
def contract?(nil), do: true
def formatted_usd(%Address{fetched_coin_balance: nil}, _), do: nil
def formatted_usd(%Address{fetched_coin_balance: balance}, %Token{} = exchange_rate) do
case Wei.cast(balance) do
{:ok, wei} ->
wei
|> USD.from(exchange_rate)
|> format_usd_value()
_ ->
nil
end
end
def hash(%Address{hash: hash}) do
to_string(hash)
end

@ -1,9 +1,6 @@
defmodule BlockScoutWeb.ChainView do
use BlockScoutWeb, :view
alias Explorer.ExchangeRates.Token
alias BlockScoutWeb.ExchangeRates.USD
def encode_market_history_data(market_history_data) do
market_history_data
|> Enum.map(fn day -> Map.take(day, [:closing_price, :date]) end)
@ -13,16 +10,4 @@ defmodule BlockScoutWeb.ChainView do
_ -> []
end
end
def format_exchange_rate(%Token{usd_value: usd_value}) do
usd_value
|> USD.from()
|> format_usd_value()
end
def format_market_cap(%Token{market_cap_usd: market_cap}) do
market_cap
|> USD.from()
|> format_usd_value()
end
end

@ -3,46 +3,8 @@ defmodule BlockScoutWeb.CurrencyHelpers do
Helper functions for interacting with `t:BlockScoutWeb.ExchangeRates.USD.t/0` values.
"""
alias BlockScoutWeb.ExchangeRates.USD
alias BlockScoutWeb.Cldr.Number
@doc """
Formats a `BlockScoutWeb.ExchangeRates.USD` value into USD and applies a unit label.
## Examples
iex> format_usd_value(%USD{value: Decimal.new(0.0000001)})
"< $0.000001 USD"
iex> format_usd_value(%USD{value: Decimal.new(0.123456789)})
"$0.123457 USD"
iex> format_usd_value(%USD{value: Decimal.new(0.1234)})
"$0.123400 USD"
iex> format_usd_value(%USD{value: Decimal.new(1.23456789)})
"$1.23 USD"
iex> format_usd_value(%USD{value: Decimal.new(1.2)})
"$1.20 USD"
iex> format_usd_value(%USD{value: Decimal.new(123456.789)})
"$123,457 USD"
"""
@spec format_usd_value(USD.t() | nil) :: binary() | nil
def format_usd_value(nil), do: nil
def format_usd_value(%USD{value: nil}), do: nil
def format_usd_value(%USD{value: value}) do
cond do
Decimal.cmp(value, "0.000001") == :lt -> "< $0.000001 USD"
Decimal.cmp(value, 1) == :lt -> "$#{Number.to_string!(value, format: "0.000000")} USD"
Decimal.cmp(value, 100_000) == :lt -> "$#{Number.to_string!(value, format: "#,###.00")} USD"
true -> "$#{Number.to_string!(value, format: "#,###")} USD"
end
end
@doc """
Formats the given integer value to a currency format.

@ -4,9 +4,7 @@ defmodule BlockScoutWeb.TransactionView do
alias Cldr.Number
alias Explorer.Chain
alias Explorer.Chain.{Address, InternalTransaction, Transaction, Wei}
alias Explorer.ExchangeRates.Token
alias BlockScoutWeb.{AddressView, BlockView}
alias BlockScoutWeb.ExchangeRates.USD
import BlockScoutWeb.Gettext
@ -30,14 +28,18 @@ defmodule BlockScoutWeb.TransactionView do
def to_address_hash(%Transaction{to_address: %Address{hash: address_hash}}), do: address_hash
def fee(%Transaction{} = transaction) do
{_, value} = Chain.fee(transaction, :wei)
value
end
def formatted_fee(%Transaction{} = transaction, opts) do
transaction
|> Chain.fee(:wei)
|> fee_to_currency(opts)
|> fee_to_denomination(opts)
|> case do
{_, nil} -> nil
{:actual, value} -> value
{:maximum, value} -> "<= " <> value
{:maximum, value} -> "#{gettext("Max of")} #{value}"
end
end
@ -80,12 +82,6 @@ defmodule BlockScoutWeb.TransactionView do
end
end
def formatted_usd_value(%Transaction{value: nil}, _token), do: nil
def formatted_usd_value(%Transaction{value: value}, token) do
format_usd_value(USD.from(value, token))
end
defdelegate formatted_timestamp(block), to: BlockView
def gas(%type{gas: gas}) when is_transaction_type(type) do
@ -137,23 +133,6 @@ defmodule BlockScoutWeb.TransactionView do
format_wei_value(value, :ether, include_unit_label: include_label?)
end
defp fee_to_currency(fee, options) do
case Keyword.fetch(options, :exchange_rate) do
{:ok, exchange_rate} -> fee_to_usd(fee, exchange_rate)
:error -> fee_to_denomination(fee, options)
end
end
defp fee_to_usd({fee_type, fee}, %Token{} = exchange_rate) do
formatted =
fee
|> Wei.from(:wei)
|> USD.from(exchange_rate)
|> format_usd_value()
{fee_type, formatted}
end
defp fee_to_denomination({fee_type, fee}, opts) do
denomination = Keyword.get(opts, :denomination)
include_label? = Keyword.get(opts, :include_label, true)

@ -6,7 +6,7 @@ msgstr ""
msgid "Block"
msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:58
#: lib/block_scout_web/templates/chain/show.html.eex:56
#: lib/block_scout_web/templates/layout/_topnav.html.eex:13
msgid "Blocks"
msgstr ""
@ -43,14 +43,14 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:36
#: lib/block_scout_web/templates/chain/show.html.eex:75
#: lib/block_scout_web/templates/chain/show.html.eex:73
#: lib/block_scout_web/templates/layout/_topnav.html.eex:18
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:46
#: lib/block_scout_web/templates/transaction/index.html.eex:56
msgid "Transactions"
msgstr ""
#: lib/block_scout_web/templates/transaction/overview.html.eex:82
#: lib/block_scout_web/templates/transaction/overview.html.eex:84
msgid "Value"
msgstr ""
@ -110,7 +110,7 @@ msgstr ""
msgid "Cumulative Gas Used"
msgstr ""
#: lib/block_scout_web/templates/transaction/overview.html.eex:93
#: lib/block_scout_web/templates/transaction/overview.html.eex:95
msgid "Gas"
msgstr ""
@ -118,7 +118,7 @@ msgstr ""
msgid "Gas Price"
msgstr ""
#: lib/block_scout_web/templates/transaction/overview.html.eex:63
#: lib/block_scout_web/templates/transaction/overview.html.eex:65
msgid "Input"
msgstr ""
@ -130,7 +130,7 @@ msgstr ""
msgid "%{count} transactions in this block"
msgstr ""
#: lib/block_scout_web/views/address_view.ex:15
#: lib/block_scout_web/views/address_view.ex:12
msgid "Address"
msgstr ""
@ -146,7 +146,7 @@ msgstr ""
msgid "Overview"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:79
#: lib/block_scout_web/views/transaction_view.ex:81
msgid "Success"
msgstr ""
@ -198,8 +198,8 @@ msgstr ""
#: 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/transaction/overview.html.eex:40
#: lib/block_scout_web/views/transaction_view.ex:44
#: lib/block_scout_web/views/transaction_view.ex:78
#: lib/block_scout_web/views/transaction_view.ex:46
#: lib/block_scout_web/views/transaction_view.ex:80
msgid "Pending"
msgstr ""
@ -267,11 +267,11 @@ msgstr ""
msgid "Next Page"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:76
#: lib/block_scout_web/views/transaction_view.ex:78
msgid "Failed"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:77
#: lib/block_scout_web/views/transaction_view.ex:79
msgid "Out of Gas"
msgstr ""
@ -291,7 +291,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:22
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:68
#: lib/block_scout_web/templates/transaction/_tile.html.eex:24
#: lib/block_scout_web/templates/transaction/overview.html.eex:82
#: lib/block_scout_web/templates/transaction/overview.html.eex:84
#: lib/block_scout_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16
#: lib/block_scout_web/views/wei_helpers.ex:72
msgid "Ether"
@ -391,11 +391,13 @@ msgstr ""
msgid "Avg Block Time"
msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:17
#: lib/block_scout_web/templates/layout/app.html.eex:24
msgid "Market Cap"
msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:10
#: lib/block_scout_web/templates/layout/app.html.eex:25
msgid "Price"
msgstr ""
@ -439,7 +441,7 @@ msgstr ""
msgid "Total Gas Used"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:124
#: lib/block_scout_web/views/transaction_view.ex:120
msgid "Transaction"
msgstr ""
@ -462,7 +464,7 @@ msgstr ""
msgid "Contract"
msgstr ""
#: lib/block_scout_web/views/address_view.ex:13
#: lib/block_scout_web/views/address_view.ex:10
msgid "Contract Address"
msgstr ""
@ -524,7 +526,7 @@ msgid "Newer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:122
#: lib/block_scout_web/views/transaction_view.ex:118
msgid "Contract Creation"
msgstr ""
@ -554,27 +556,27 @@ msgid "Twitter"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:57
#: lib/block_scout_web/templates/chain/show.html.eex:55
msgid "View All Blocks →"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:74
#: lib/block_scout_web/templates/chain/show.html.eex:72
msgid "View All Transactions →"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:28
#: lib/block_scout_web/templates/chain/show.html.eex:26
msgid "Average block time"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:36
#: lib/block_scout_web/templates/chain/show.html.eex:34
msgid "Total transactions"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:44
#: lib/block_scout_web/templates/chain/show.html.eex:42
msgid "Wallet addresses"
msgstr ""
@ -621,7 +623,7 @@ msgid "Contract Address Pending"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:123
#: lib/block_scout_web/views/transaction_view.ex:119
msgid "Contract Call"
msgstr ""
@ -698,7 +700,7 @@ msgid "Block Confirmations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:105
#: lib/block_scout_web/templates/transaction/overview.html.eex:107
msgid "Limit"
msgstr ""
@ -714,14 +716,14 @@ msgid "There are no logs for this transaction."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:98
#: lib/block_scout_web/templates/transaction/overview.html.eex:100
msgid "Used"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:121
#: lib/block_scout_web/views/transaction_view.ex:117
msgid "Token Transfer"
msgstr ""
@ -739,7 +741,7 @@ msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:84
#: lib/block_scout_web/templates/chain/show.html.eex:71
#: lib/block_scout_web/templates/chain/show.html.eex:69
#: lib/block_scout_web/templates/transaction/index.html.eex:48
msgid "More transactions have come in"
msgstr ""
@ -821,22 +823,15 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:16
#: lib/block_scout_web/templates/address/_token_holdings.html.eex:16
#: lib/block_scout_web/templates/address/_balance_card.html.eex:19
msgid "Error tryng to fetch balances."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:12
#: lib/block_scout_web/templates/address/_token_holdings.html.eex:12
#: lib/block_scout_web/templates/address/_balance_card.html.eex:15
msgid "Fetching tokens..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_token_holdings.html.eex:3
msgid "Token Holdings"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:38
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:53
@ -995,3 +990,13 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tile.html.eex:68
msgid "View Less Transfers"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:23
msgid "Less than"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:42
msgid "Max of"
msgstr ""

@ -18,7 +18,7 @@ msgstr "Age"
msgid "Block"
msgstr "Block"
#: lib/block_scout_web/templates/chain/show.html.eex:58
#: lib/block_scout_web/templates/chain/show.html.eex:56
#: lib/block_scout_web/templates/layout/_topnav.html.eex:13
msgid "Blocks"
msgstr "Blocks"
@ -55,14 +55,14 @@ msgstr "BlockScout"
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:36
#: lib/block_scout_web/templates/chain/show.html.eex:75
#: lib/block_scout_web/templates/chain/show.html.eex:73
#: lib/block_scout_web/templates/layout/_topnav.html.eex:18
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:46
#: lib/block_scout_web/templates/transaction/index.html.eex:56
msgid "Transactions"
msgstr "Transactions"
#: lib/block_scout_web/templates/transaction/overview.html.eex:82
#: lib/block_scout_web/templates/transaction/overview.html.eex:84
msgid "Value"
msgstr "Value"
@ -122,7 +122,7 @@ msgstr "Transaction Details"
msgid "Cumulative Gas Used"
msgstr "Cumulative Gas Used"
#: lib/block_scout_web/templates/transaction/overview.html.eex:93
#: lib/block_scout_web/templates/transaction/overview.html.eex:95
msgid "Gas"
msgstr "Gas"
@ -130,7 +130,7 @@ msgstr "Gas"
msgid "Gas Price"
msgstr "Gas Price"
#: lib/block_scout_web/templates/transaction/overview.html.eex:63
#: lib/block_scout_web/templates/transaction/overview.html.eex:65
msgid "Input"
msgstr "Input"
@ -142,7 +142,7 @@ msgstr "%{confirmations} block confirmations"
msgid "%{count} transactions in this block"
msgstr "%{count} transactions in this block"
#: lib/block_scout_web/views/address_view.ex:15
#: lib/block_scout_web/views/address_view.ex:12
msgid "Address"
msgstr "Address"
@ -158,7 +158,7 @@ msgstr "From"
msgid "Overview"
msgstr "Overview"
#: lib/block_scout_web/views/transaction_view.ex:79
#: lib/block_scout_web/views/transaction_view.ex:81
msgid "Success"
msgstr "Success"
@ -210,8 +210,8 @@ msgstr "Showing %{count} Transactions"
#: 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/transaction/overview.html.eex:40
#: lib/block_scout_web/views/transaction_view.ex:44
#: lib/block_scout_web/views/transaction_view.ex:78
#: lib/block_scout_web/views/transaction_view.ex:46
#: lib/block_scout_web/views/transaction_view.ex:80
msgid "Pending"
msgstr "Pending"
@ -279,11 +279,11 @@ msgstr ""
msgid "Next Page"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:76
#: lib/block_scout_web/views/transaction_view.ex:78
msgid "Failed"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:77
#: lib/block_scout_web/views/transaction_view.ex:79
msgid "Out of Gas"
msgstr ""
@ -303,7 +303,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:22
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:68
#: lib/block_scout_web/templates/transaction/_tile.html.eex:24
#: lib/block_scout_web/templates/transaction/overview.html.eex:82
#: lib/block_scout_web/templates/transaction/overview.html.eex:84
#: lib/block_scout_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16
#: lib/block_scout_web/views/wei_helpers.ex:72
msgid "Ether"
@ -403,11 +403,13 @@ msgstr ""
msgid "Avg Block Time"
msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:18
#: lib/block_scout_web/templates/chain/show.html.eex:17
#: lib/block_scout_web/templates/layout/app.html.eex:24
msgid "Market Cap"
msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:10
#: lib/block_scout_web/templates/layout/app.html.eex:25
msgid "Price"
msgstr ""
@ -451,7 +453,7 @@ msgstr ""
msgid "Total Gas Used"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:124
#: lib/block_scout_web/views/transaction_view.ex:120
msgid "Transaction"
msgstr ""
@ -474,7 +476,7 @@ msgstr ""
msgid "Contract"
msgstr ""
#: lib/block_scout_web/views/address_view.ex:13
#: lib/block_scout_web/views/address_view.ex:10
msgid "Contract Address"
msgstr ""
@ -536,7 +538,7 @@ msgid "Newer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:122
#: lib/block_scout_web/views/transaction_view.ex:118
msgid "Contract Creation"
msgstr ""
@ -566,27 +568,27 @@ msgid "Twitter"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:57
#: lib/block_scout_web/templates/chain/show.html.eex:55
msgid "View All Blocks →"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:74
#: lib/block_scout_web/templates/chain/show.html.eex:72
msgid "View All Transactions →"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:28
#: lib/block_scout_web/templates/chain/show.html.eex:26
msgid "Average block time"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:36
#: lib/block_scout_web/templates/chain/show.html.eex:34
msgid "Total transactions"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:44
#: lib/block_scout_web/templates/chain/show.html.eex:42
msgid "Wallet addresses"
msgstr ""
@ -633,7 +635,7 @@ msgid "Contract Address Pending"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:123
#: lib/block_scout_web/views/transaction_view.ex:119
msgid "Contract Call"
msgstr ""
@ -710,7 +712,7 @@ msgid "Block Confirmations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:105
#: lib/block_scout_web/templates/transaction/overview.html.eex:107
msgid "Limit"
msgstr ""
@ -726,14 +728,14 @@ msgid "There are no logs for this transaction."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:98
#: lib/block_scout_web/templates/transaction/overview.html.eex:100
msgid "Used"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:121
#: lib/block_scout_web/views/transaction_view.ex:117
msgid "Token Transfer"
msgstr ""
@ -751,7 +753,7 @@ msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:84
#: lib/block_scout_web/templates/chain/show.html.eex:71
#: lib/block_scout_web/templates/chain/show.html.eex:69
#: lib/block_scout_web/templates/transaction/index.html.eex:48
msgid "More transactions have come in"
msgstr ""
@ -833,22 +835,15 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:16
#: lib/block_scout_web/templates/address/_token_holdings.html.eex:16
#: lib/block_scout_web/templates/address/_balance_card.html.eex:19
msgid "Error tryng to fetch balances."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:12
#: lib/block_scout_web/templates/address/_token_holdings.html.eex:12
#: lib/block_scout_web/templates/address/_balance_card.html.eex:15
msgid "Fetching tokens..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_token_holdings.html.eex:3
msgid "Token Holdings"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:38
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:53
@ -1007,3 +1002,13 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tile.html.eex:68
msgid "View Less Transfers"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/app.html.eex:23
msgid "Less than"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:42
msgid "Max of"
msgstr ""

@ -1,48 +0,0 @@
defmodule BlockScoutWeb.ExchangeRates.USDTest do
use ExUnit.Case, async: true
alias BlockScoutWeb.ExchangeRates.USD
alias Explorer.ExchangeRates.Token
alias Explorer.Chain.Wei
describe "from/2" do
test "with nil wei returns null object" do
token = %Token{usd_value: Decimal.new(0.5)}
assert USD.null() == USD.from(nil, token)
end
test "with nil token returns nil" do
wei = %Wei{value: Decimal.new(10_000_000_000_000)}
assert USD.null() == USD.from(wei, nil)
end
test "without a wei value returns nil" do
wei = %Wei{value: nil}
token = %Token{usd_value: Decimal.new(0.5)}
assert USD.null() == USD.from(wei, token)
end
test "without an exchange rate returns nil" do
wei = %Wei{value: Decimal.new(10_000_000_000_000)}
token = %Token{usd_value: nil}
assert USD.null() == USD.from(wei, token)
end
test "returns formatted usd value" do
wei = %Wei{value: Decimal.new(10_000_000_000_000)}
token = %Token{usd_value: Decimal.new(0.5)}
assert %USD{value: Decimal.new(0.000005)} == USD.from(wei, token)
end
test "returns USD struct from decimal usd value" do
value = Decimal.new(0.000005)
assert %USD{value: ^value} = USD.from(value)
end
end
end

@ -6,7 +6,6 @@ defmodule BlockScoutWeb.ChainPage do
import Wallaby.Query, only: [css: 1, css: 2]
alias Explorer.Chain.Transaction
alias BlockScoutWeb.ChainView
def blocks(count: count) do
css("[data-selector='chain-block']", count: count)
@ -16,10 +15,6 @@ defmodule BlockScoutWeb.ChainPage do
css("[data-test='contract-creation'] [data-address-hash='#{hash}']")
end
def exchange_rate(token) do
css("[data-selector='exchange-rate']", text: ChainView.format_exchange_rate(token))
end
def non_loaded_transaction_count(count) do
css("[data-selector='channel-batching-count']", text: count)
end

@ -3,7 +3,6 @@ defmodule BlockScoutWeb.AddressViewTest do
alias Explorer.Chain.Data
alias BlockScoutWeb.AddressView
alias Explorer.ExchangeRates.Token
describe "contract?/1" do
test "with a smart contract" do
@ -18,26 +17,6 @@ defmodule BlockScoutWeb.AddressViewTest do
end
end
describe "formatted_usd/2" do
test "without a fetched_coin_balance returns nil" do
address = build(:address, fetched_coin_balance: nil)
token = %Token{usd_value: Decimal.new(0.5)}
assert nil == AddressView.formatted_usd(address, token)
end
test "without a usd_value returns nil" do
address = build(:address)
token = %Token{usd_value: nil}
assert nil == AddressView.formatted_usd(address, token)
end
test "returns formatted usd value" do
address = build(:address, fetched_coin_balance: 10_000_000_000_000)
token = %Token{usd_value: Decimal.new(0.5)}
assert "$0.000005 USD" == AddressView.formatted_usd(address, token)
end
end
describe "qr_code/1" do
test "it returns an encoded value" do
address = build(:address)

@ -1,7 +1,6 @@
defmodule BlockScoutWeb.ChainViewTest do
use BlockScoutWeb.ConnCase, async: true
alias Explorer.ExchangeRates.Token
alias BlockScoutWeb.ChainView
describe "encode_market_history_data/1" do
@ -17,22 +16,4 @@ defmodule BlockScoutWeb.ChainViewTest do
ChainView.encode_market_history_data(market_history_data)
end
end
describe "format_exchange_rate/1" do
test "returns a formatted usd value from a `Token`'s usd_value" do
token = %Token{usd_value: Decimal.new(5.45)}
assert "$5.45 USD" == ChainView.format_exchange_rate(token)
assert nil == ChainView.format_exchange_rate(%Token{usd_value: nil})
end
end
describe "format_market_cap/1" do
test "returns a formatted usd value from a `Token`'s market_cap_usd" do
token = %Token{market_cap_usd: Decimal.new(5.4)}
assert "$5.40 USD" == ChainView.format_market_cap(token)
assert nil == ChainView.format_market_cap(%Token{market_cap_usd: nil})
end
end
end

@ -2,18 +2,9 @@ defmodule BlockScoutWeb.CurrencyHelpersTest do
use ExUnit.Case
alias BlockScoutWeb.CurrencyHelpers
alias BlockScoutWeb.ExchangeRates.USD
doctest BlockScoutWeb.CurrencyHelpers, import: true
test "with nil it returns nil" do
assert nil == CurrencyHelpers.format_usd_value(nil)
end
test "with USD.null() it returns nil" do
assert nil == CurrencyHelpers.format_usd_value(USD.null())
end
describe "format_according_to_decimals/1" do
test "formats the amount as value considering the given decimals" do
amount = Decimal.new(205_000_000_000_000)

@ -2,7 +2,6 @@ defmodule BlockScoutWeb.TransactionViewTest do
use BlockScoutWeb.ConnCase, async: true
alias Explorer.Chain.Wei
alias Explorer.ExchangeRates.Token
alias Explorer.Repo
alias BlockScoutWeb.TransactionView
@ -18,21 +17,16 @@ defmodule BlockScoutWeb.TransactionViewTest do
gas_used: nil
)
token = %Token{usd_value: Decimal.new(0.50)}
expected_value = "<= 0.009 POA"
expected_value = "max of 0.009 POA"
assert expected_value == TransactionView.formatted_fee(transaction, denomination: :ether)
assert "<= $0.004500 USD" == TransactionView.formatted_fee(transaction, exchange_rate: token)
end
test "with fee and exchange_rate" do
test "with fee" do
{:ok, gas_price} = Wei.cast(3_000_000_000)
transaction = build(:transaction, gas_price: gas_price, gas_used: Decimal.new(1_034_234.0))
token = %Token{usd_value: Decimal.new(0.50)}
expected_value = "0.003102702 POA"
assert expected_value == TransactionView.formatted_fee(transaction, denomination: :ether)
assert "$0.001551 USD" == TransactionView.formatted_fee(transaction, exchange_rate: token)
end
test "with fee but no available exchange_rate" do

@ -5,11 +5,26 @@ defmodule Indexer.TokenBalances do
alias Explorer.Token.BalanceReader
@doc """
Fetches TokenBalances from specific Addresses and Blocks in the Blockchain
Every `TokenBalance` is fetched asynchronously, but in case an exception is raised (such as a
timeout) during the RPC call the particular TokenBalance request is ignored.
## token_balances
It is a list of a Map so that each map must have:
* `token_contract_address_hash` - The contract address that represents the Token in the blockchain.
* `address_hash` - The address_hash that we want to know the balance.
* `block_number` - The block number that the address_hash has the balance.
"""
def fetch_token_balances_from_blockchain(token_balances) do
result =
token_balances
|> Task.async_stream(&fetch_token_balance/1)
|> Enum.map(&format_result/1)
|> Task.async_stream(&fetch_token_balance/1, on_timeout: :kill_task)
|> Stream.map(&format_task_results/1)
|> Enum.filter(&ignore_request_with_timeouts/1)
{:ok, result}
end
@ -34,5 +49,9 @@ defmodule Indexer.TokenBalances do
Map.merge(token_balance, %{value: nil, value_fetched_at: nil})
end
def format_result({_, token_balance}), do: token_balance
def format_task_results({:exit, :timeout}), do: {:error, :timeout}
def format_task_results({:ok, token_balance}), do: token_balance
def ignore_request_with_timeouts({:error, :timeout}), do: false
def ignore_request_with_timeouts(_token_balance), do: true
end

@ -60,6 +60,32 @@ defmodule Indexer.TokenBalancesTest do
value_fetched_at: nil
} = List.first(result)
end
test "ignores results that raised :timeout" do
address = insert(:address)
token = insert(:token, contract_address: build(:contract_address))
address_hash_string = Hash.to_string(address.hash)
token_balance_params = [
%{
token_contract_address_hash: Hash.to_string(token.contract_address_hash),
address_hash: address_hash_string,
block_number: 1_000
},
%{
token_contract_address_hash: Hash.to_string(token.contract_address_hash),
address_hash: address_hash_string,
block_number: 1_001
}
]
get_balance_from_blockchain()
get_balance_from_blockchain_with_timeout()
{:ok, result} = TokenBalances.fetch_token_balances_from_blockchain(token_balance_params)
assert length(result) == 1
end
end
defp get_balance_from_blockchain() do
@ -79,6 +105,16 @@ defmodule Indexer.TokenBalancesTest do
)
end
defp get_balance_from_blockchain_with_timeout() do
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [%{id: _, method: _, params: [%{data: _, to: _}, _]}], _options ->
:timer.sleep(5001)
end
)
end
defp get_balance_from_blockchain_with_error() do
expect(
EthereumJSONRPC.Mox,

@ -1,7 +1,7 @@
{
"coverage_options": {
"treat_no_relevant_lines_as_covered": true,
"minimum_coverage": 94.5
"minimum_coverage": 88
},
"terminal_options": {
"file_column_width": 120

Loading…
Cancel
Save