parent
32107089d5
commit
1614065981
@ -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…
Reference in new issue