parent
7e13e38f3a
commit
4a63b978fd
@ -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…
Reference in new issue