feat: calculate RSK market cap

pull/2199/head
zachdaniel 6 years ago
parent 7e13e38f3a
commit 4a63b978fd
  1. 11
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  2. 2
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  3. 8
      apps/block_scout_web/lib/block_scout_web/views/chain_view.ex
  4. 9
      apps/explorer/config/config.exs
  5. 103
      apps/explorer/lib/explorer/chain/supply/rsk.ex
  6. 139
      apps/explorer/test/explorer/chain/supply/rsk_test.exs

@ -4,6 +4,7 @@ defmodule BlockScoutWeb.ChainController do
alias BlockScoutWeb.ChainView
alias Explorer.{Chain, PagingOptions, Repo}
alias Explorer.Chain.{Address, Block, Transaction}
alias Explorer.Chain.Supply.RSK
alias Explorer.Counters.AverageBlockTime
alias Explorer.ExchangeRates.Token
alias Explorer.Market
@ -13,6 +14,15 @@ defmodule BlockScoutWeb.ChainController do
transaction_estimated_count = Chain.transaction_estimated_count()
block_count = Chain.block_estimated_count()
market_cap_calculation =
case Application.get_env(:explorer, :supply) do
RSK ->
RSK
_ ->
:standard
end
exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
render(
@ -22,6 +32,7 @@ defmodule BlockScoutWeb.ChainController do
average_block_time: AverageBlockTime.average_block_time(),
exchange_rate: exchange_rate,
chart_data_path: market_history_chart_path(conn, :show),
market_cap_calculation: market_cap_calculation,
transaction_estimated_count: transaction_estimated_count,
transactions_path: recent_transactions_path(conn, :index),
block_count: block_count

@ -30,7 +30,7 @@
<span class="dashboard-banner-chart-legend-label">
<%= gettext "Market Cap" %>
</span>
<span class="dashboard-banner-chart-legend-value" data-selector="market-cap" data-usd-value="<%= @exchange_rate.market_cap_usd %>">
<span class="dashboard-banner-chart-legend-value" data-selector="market-cap" data-usd-value="<%= market_cap(@market_cap_calculation, @exchange_rate) %>">
</span>
</div>
</div>

@ -2,4 +2,12 @@ defmodule BlockScoutWeb.ChainView do
use BlockScoutWeb, :view
alias BlockScoutWeb.LayoutView
defp market_cap(:standard, exchange_rate) do
exchange_rate.market_cap_usd
end
defp market_cap(module, exchange_rate) do
module.market_cap(exchange_rate)
end
end

@ -71,8 +71,15 @@ else
config :explorer, Explorer.Staking.EpochCounter, enabled: false
end
if System.get_env("SUPPLY_MODULE") == "TokenBridge" do
case System.get_env("SUPPLY_MODULE") do
"TokenBridge" ->
config :explorer, supply: Explorer.Chain.Supply.TokenBridge
"rsk" ->
config :explorer, supply: Explorer.Chain.Supply.RSK
_ ->
:ok
end
if System.get_env("SOURCE_MODULE") == "TransactionAndLog" do

@ -0,0 +1,103 @@
defmodule Explorer.Chain.Supply.RSK do
@moduledoc """
Defines the supply API for calculating supply for coins from RSK.
"""
use Explorer.Chain.Supply
import Ecto.Query, only: [from: 2]
alias Explorer.Chain.Address.CoinBalance
alias Explorer.Chain.{Block, Wei}
alias Explorer.ExchangeRates.Token
alias Explorer.{Market, Repo}
def market_cap(exchange_rate) do
circulating() * exchange_rate.usd_value
end
@doc "Equivalent to getting the circulating value "
def supply_for_days(days) do
now = Timex.now()
balances_query =
from(balance in CoinBalance,
join: block in Block,
on: block.number == balance.block_number,
where: block.consensus == true,
where: balance.address_hash == ^"0x0000000000000000000000000000000001000006",
where: block.timestamp > ^Timex.shift(now, days: -days),
distinct: fragment("date_trunc('day', ?)", block.timestamp),
select: {block.timestamp, balance.value}
)
balance_before_query =
from(balance in CoinBalance,
join: block in Block,
on: block.number == balance.block_number,
where: block.consensus == true,
where: balance.address_hash == ^"0x0000000000000000000000000000000001000006",
where: block.timestamp <= ^Timex.shift(Timex.now(), days: -days),
order_by: [desc: block.timestamp],
limit: 1,
select: balance.value
)
by_day =
balances_query
|> Repo.all()
|> Enum.into(%{}, fn {timestamp, value} ->
{Timex.to_date(timestamp), value}
end)
starting = Repo.one(balance_before_query) || wei!(0)
result =
-days..0
|> Enum.reduce({%{}, starting.value}, fn i, {days, last} ->
date =
now
|> Timex.shift(days: i)
|> Timex.to_date()
case Map.get(by_day, date) do
nil ->
{Map.put(days, date, last), last}
value ->
{Map.put(days, date, value.value), value.value}
end
end)
|> elem(0)
{:ok, result}
end
def circulating do
query =
from(balance in CoinBalance,
join: block in Block,
on: block.number == balance.block_number,
where: block.consensus == true,
where: balance.address_hash == ^"0x0000000000000000000000000000000001000006",
order_by: [desc: block.timestamp],
limit: 1,
select: balance.value
)
Repo.one(query) || wei!(0)
end
defp wei!(value) do
{:ok, wei} = Wei.cast(value)
wei
end
def total do
21_000_000
end
def exchange_rate do
Market.get_exchange_rate(Explorer.coin()) || Token.null()
end
end

@ -0,0 +1,139 @@
defmodule Explorer.Chain.Supply.RSKTest do
use Explorer.DataCase
alias Explorer.Chain.Supply.RSK
alias Explorer.Chain.Wei
@coin_address "0x0000000000000000000000000000000001000006"
defp wei!(value) do
{:ok, wei} = Wei.cast(value)
wei
end
test "total is 21_000_000" do
assert RSK.total() == 21_000_000
end
describe "circulating/0" do
test "with no balance" do
assert RSK.circulating() == wei!(0)
end
test "with a balance" do
address = insert(:address, hash: @coin_address)
insert(:block, number: 0)
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 0)
assert RSK.circulating() == wei!(10)
end
end
defp date(now, shift \\ []) do
now
|> Timex.shift(shift)
|> Timex.to_date()
end
defp dec(number) do
Decimal.new(number)
end
describe "supply_for_days/1" do
test "when there is no balance" do
now = Timex.now()
assert RSK.supply_for_days(2) ==
{:ok,
%{
date(now, days: -2) => dec(0),
date(now, days: -1) => dec(0),
date(now) => dec(0)
}}
end
test "when there is a single balance before the days, that balance is used" do
address = insert(:address, hash: @coin_address)
now = Timex.now()
insert(:block, number: 0, timestamp: Timex.shift(now, days: -10))
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 0)
assert RSK.supply_for_days(2) ==
{:ok,
%{
date(now, days: -2) => dec(10),
date(now, days: -1) => dec(10),
date(now) => dec(10)
}}
end
test "when there is a balance for one of the days, days after it use that balance" do
address = insert(:address, hash: @coin_address)
now = Timex.now()
insert(:block, number: 0, timestamp: Timex.shift(now, days: -10))
insert(:block, number: 1, timestamp: Timex.shift(now, days: -1))
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 0)
insert(:fetched_balance, value: 20, address_hash: address.hash, block_number: 1)
assert RSK.supply_for_days(2) ==
{:ok,
%{
date(now, days: -2) => dec(10),
date(now, days: -1) => dec(20),
date(now) => dec(20)
}}
end
test "when there is a balance for the first day, that balance is used" do
address = insert(:address, hash: @coin_address)
now = Timex.now()
insert(:block, number: 0, timestamp: Timex.shift(now, days: -10))
insert(:block, number: 1, timestamp: Timex.shift(now, days: -2))
insert(:block, number: 2, timestamp: Timex.shift(now, days: -1))
insert(:fetched_balance, value: 5, address_hash: address.hash, block_number: 0)
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 1)
insert(:fetched_balance, value: 20, address_hash: address.hash, block_number: 2)
assert RSK.supply_for_days(2) ==
{:ok,
%{
date(now, days: -2) => dec(10),
date(now, days: -1) => dec(20),
date(now) => dec(20)
}}
end
test "when there is a balance for all days, they are each used correctly" do
address = insert(:address, hash: @coin_address)
now = Timex.now()
insert(:block, number: 0, timestamp: Timex.shift(now, days: -10))
insert(:block, number: 1, timestamp: Timex.shift(now, days: -2))
insert(:block, number: 2, timestamp: Timex.shift(now, days: -1))
insert(:block, number: 3, timestamp: now)
insert(:fetched_balance, value: 5, address_hash: address.hash, block_number: 0)
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 1)
insert(:fetched_balance, value: 20, address_hash: address.hash, block_number: 2)
insert(:fetched_balance, value: 30, address_hash: address.hash, block_number: 3)
assert RSK.supply_for_days(2) ==
{:ok,
%{
date(now, days: -2) => dec(10),
date(now, days: -1) => dec(20),
date(now) => dec(30)
}}
end
end
end
Loading…
Cancel
Save