use smart contracts to calculate xDai marketcap

pull/1396/head
Ayrat Badykov 6 years ago
parent 32107089d5
commit 1614065981
No known key found for this signature in database
GPG Key ID: B44668E265E9396F
  1. 133
      apps/explorer/lib/explorer/chain/supply/token_bridge.ex
  2. 66
      apps/explorer/test/explorer/chain/supply/token_bridge_test.exs

@ -0,0 +1,133 @@
defmodule Explorer.Chain.Supply.TokenBridge do
@moduledoc """
Defines the supply API for calculating the supply based on Token Bridge.
"""
use Explorer.Chain.Supply
alias Explorer.Chain.Wei
alias Explorer.SmartContract.Reader
@token_bridge_contract_address "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
@total_burned_coins_abi %{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "uint256", "name" => ""}],
"name" => "totalBurntCoins",
"inputs" => [],
"constant" => true
}
@total_burned_coins_params %{"totalBurntCoins" => []}
@block_reward_contract_address "0x867305d19606aadba405ce534e303d0e225f9556"
@total_minted_coins_abi %{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "uint256", "name" => ""}],
"name" => "mintedTotally",
"inputs" => [],
"constant" => true
}
@total_minted_coins_params %{"mintedTotally" => []}
@ets_table :token_bridge_contract_coin_cache
# 30 minutes
@cache_period 1_000 * 60 * 30
@cache_key "coins_with_period"
def circulating, do: total_coins()
def total, do: total_coins()
def total_coins(opts \\ []) do
cache_period = Keyword.get(opts, :cache_period) || @cache_period
cached_total_coins(cache_period)
end
defp minted_coins do
call_contract(@block_reward_contract_address, @total_minted_coins_abi, @total_minted_coins_params)
end
defp burned_coins do
call_contract(@token_bridge_contract_address, @total_burned_coins_abi, @total_burned_coins_params)
end
defp call_contract(address, abi, params) do
abi = [abi]
method_name =
params
|> Enum.map(fn {key, _value} -> key end)
|> List.first()
value =
case Reader.query_contract(address, abi, params) do
%{^method_name => {:ok, [result]}} -> result
_ -> 0
end
%Wei{value: Decimal.new(value)}
end
def cached_total_coins(cache_period) do
setup_cache()
{value, cache_time} = cached_values()
if current_time() - cache_time > cache_period do
{current_value, _} = update_cache()
current_value
else
value
end
end
defp cached_values do
cache_key = @cache_key
case :ets.lookup(@ets_table, @cache_key) do
[{^cache_key, {binary_coins, time}}] ->
coins = :erlang.binary_to_term(binary_coins)
{coins, time}
_ ->
update_cache()
end
end
defp update_cache do
current_total_coins =
minted_coins()
|> Wei.sub(burned_coins())
|> Wei.to(:ether)
binary_total_coins = :erlang.term_to_binary(current_total_coins)
current_time = current_time()
:ets.insert(@ets_table, {@cache_key, {binary_total_coins, current_time}})
{current_total_coins, current_time}
end
defp setup_cache do
if :ets.whereis(@ets_table) == :undefined do
:ets.new(@ets_table, [
:set,
:named_table,
:public,
write_concurrency: true
])
end
end
defp current_time do
utc_now = DateTime.utc_now()
DateTime.to_unix(utc_now, :millisecond)
end
end

@ -0,0 +1,66 @@
defmodule Explorer.Chain.Supply.TokenBridgeTest do
use EthereumJSONRPC.Case, async: false
import Mox
alias Explorer.Chain.Supply.TokenBridge
@moduletag :capture_log
setup :set_mox_global
setup :verify_on_exit!
describe "total_coins/1" do
@tag :no_parity
@tag :no_geth
test "calculates total coins", %{json_rpc_named_arguments: json_rpc_named_arguments} do
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, fn [
%{
id: "mintedTotally",
method: "eth_call",
params: [
%{data: "0x553a5c85", to: "0x867305d19606aadba405ce534e303d0e225f9556"},
"latest"
]
}
],
_options ->
{:ok,
[
%{
id: "mintedTotally",
jsonrpc: "2.0",
result: "0x00000000000000000000000000000000000000000000042aa8fe57ebb112dcc8"
}
]}
end)
|> expect(:json_rpc, fn [
%{
id: "totalBurntCoins",
jsonrpc: "2.0",
method: "eth_call",
params: [
%{data: "0x0e8162ba", to: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"},
"latest"
]
}
],
_options ->
{:ok,
[
%{
id: "totalBurntCoins",
jsonrpc: "2.0",
result: "0x00000000000000000000000000000000000000000000033cc192839185166fc6"
}
]}
end)
end
assert Decimal.round(TokenBridge.total_coins(), 2, :down) == Decimal.from_float(4388.55)
end
end
end
Loading…
Cancel
Save