diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a3bc7bc24..1e0fd3e1b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ - [#2468](https://github.com/poanetwork/blockscout/pull/2468) - fix confirmations for non consensus blocks ### Chore +- [#2662](https://github.com/poanetwork/blockscout/pull/2662) - fetch coin gecko id based on the coin symbol - [#2646](https://github.com/poanetwork/blockscout/pull/2646) - Added Xerom to list of Additional Chains using BlockScout - [#2634](https://github.com/poanetwork/blockscout/pull/2634) - add Lukso to networks dropdown - [#2617](https://github.com/poanetwork/blockscout/pull/2617) - skip cache update if there are no blocks inserted diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 056c3a991c..deefc9e3da 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -38,8 +38,6 @@ config :explorer, Explorer.Chain.Cache.BlockNumber, ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false), global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5)) -config :explorer, Explorer.ExchangeRates.Source.CoinGecko, coin_id: System.get_env("COIN_GECKO_ID", "poa-network") - balances_update_interval = if System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL") do case Integer.parse(System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL")) do diff --git a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex index 95ecafb046..9d4cfba3f4 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex @@ -44,15 +44,41 @@ defmodule Explorer.ExchangeRates.Source.CoinGecko do @impl Source def source_url do - "#{base_url()}/coins/#{coin_id()}" + {:ok, id} = coin_id() + + "#{base_url()}/coins/#{id}" end defp base_url do config(:base_url) || "https://api.coingecko.com/api/v3" end - defp coin_id do - Application.get_env(:explorer, __MODULE__)[:coin_id] + def coin_id do + url = "#{base_url()}/coins/list" + + symbol = String.downcase(Explorer.coin()) + + case HTTPoison.get(url, headers()) do + {:ok, %Response{body: body, status_code: 200}} -> + data = decode_json(body) + + symbol_data = + Enum.find(data, fn item -> + item["symbol"] == symbol + end) + + if symbol_data do + {:ok, symbol_data["id"]} + else + {:error, :not_found} + end + + {:ok, %Response{body: body, status_code: status_code}} when status_code in 400..499 -> + {:error, decode_json(body)["error"]} + + {:error, %Error{reason: reason}} -> + {:error, reason} + end end defp get_btc_price(currency \\ "usd") do diff --git a/apps/explorer/test/explorer/exchange_rates/source/coin_gecko_test.exs b/apps/explorer/test/explorer/exchange_rates/source/coin_gecko_test.exs index b4b3313d6c..76d210cd02 100644 --- a/apps/explorer/test/explorer/exchange_rates/source/coin_gecko_test.exs +++ b/apps/explorer/test/explorer/exchange_rates/source/coin_gecko_test.exs @@ -18,6 +18,46 @@ defmodule Explorer.ExchangeRates.Source.CoinGeckoTest do } """ + @coins_list """ + [ + { + "id": "poa-network", + "symbol": "poa", + "name": "POA Network" + }, + { + "id": "poc-chain", + "symbol": "pocc", + "name": "POC Chain" + }, + { + "id": "pocket-arena", + "symbol": "poc", + "name": "Pocket Arena" + }, + { + "id": "ethereum", + "symbol": "eth", + "name": "Ethereum" + }, + { + "id": "rootstock", + "symbol": "rbtc", + "name": "Rootstock RSK" + }, + { + "id": "dai", + "symbol": "dai", + "name": "Dai" + }, + { + "id": "callisto", + "symbol": "clo", + "name": "Callisto Network" + } + ] + """ + describe "format_data/1" do setup do bypass = Bypass.open() @@ -62,4 +102,65 @@ defmodule Explorer.ExchangeRates.Source.CoinGeckoTest do assert [] = CoinGecko.format_data(bad_data) end end + + describe "coin_id/0" do + setup do + bypass = Bypass.open() + Application.put_env(:explorer, CoinGecko, base_url: "http://localhost:#{bypass.port}") + + on_exit(fn -> + Application.put_env(:explorer, :coin, "POA") + end) + + {:ok, bypass: bypass} + end + + test "fetches poa coin id by default", %{bypass: bypass} do + Bypass.expect(bypass, "GET", "/coins/list", fn conn -> + Conn.resp(conn, 200, @coins_list) + end) + + assert CoinGecko.coin_id() == {:ok, "poa-network"} + end + + test "fetches eth coin id", %{bypass: bypass} do + Application.put_env(:explorer, :coin, "ETH") + + Bypass.expect(bypass, "GET", "/coins/list", fn conn -> + Conn.resp(conn, 200, @coins_list) + end) + + assert CoinGecko.coin_id() == {:ok, "ethereum"} + end + + test "fetches rbtc coin id", %{bypass: bypass} do + Application.put_env(:explorer, :coin, "RBTC") + + Bypass.expect(bypass, "GET", "/coins/list", fn conn -> + Conn.resp(conn, 200, @coins_list) + end) + + assert CoinGecko.coin_id() == {:ok, "rootstock"} + end + + test "fetches dai coin id", %{bypass: bypass} do + Application.put_env(:explorer, :coin, "DAI") + + Bypass.expect(bypass, "GET", "/coins/list", fn conn -> + Conn.resp(conn, 200, @coins_list) + end) + + assert CoinGecko.coin_id() == {:ok, "dai"} + end + + test "fetches callisto coin id", %{bypass: bypass} do + Application.put_env(:explorer, :coin, "CLO") + + Bypass.expect(bypass, "GET", "/coins/list", fn conn -> + Conn.resp(conn, 200, @coins_list) + end) + + assert CoinGecko.coin_id() == {:ok, "callisto"} + end + end end diff --git a/docs/env-variables.md b/docs/env-variables.md index 7e023ef621..5eef49d07d 100644 --- a/docs/env-variables.md +++ b/docs/env-variables.md @@ -65,6 +65,6 @@ $ export NETWORK=POA | `WEBAPP_URL` | | Link to web application instance, e.g. `http://host/path` | (empty) | v2.0.3+ | | | | `API_URL` | | Link to API instance, e.g. `http://host/path` | (empty) | v2.0.3+ | | | | `CHAIN_SPEC_PATH` | | Chain specification path (absolute file system path or url) to import block emission reward ranges and genesis account balances from | (empty) | v2.0.4+ | | | -| `COIN_GECKO_ID` | | CoinGecko coin id required for fetching an exchange rate | poa-network | v2.0.4+ | | | +| `COIN_GECKO_ID` | | CoinGecko coin id required for fetching an exchange rate | poa-network | v2.0.4+ | | master | | `EMISSION_FORMAT` | | Should be set to `POA` if you have block emission indentical to POA Network. This env var is used only if `CHAIN_SPEC_PATH` is set | `STANDARD` | v2.0.4+ | | | | `REWARDS_CONTRACT_ADDRESS` | | Emission rewards contract address. This env var is used only if `EMISSION_FORMAT` is set to `POA` | `0xeca443e8e1ab29971a45a9c57a6a9875701698a5` | v2.0.4+ | | |