From decfc08a54e3b11057450b0251d9a8c2ea960936 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 3 Sep 2019 12:46:33 +0300 Subject: [PATCH 1/5] fetch coin gecko id based on coin symbol Coin Gecko provides API with all listed coins. We can use this API to find coin id instead of setting it by hand. --- .../exchange_rates/source/coin_gecko.ex | 32 ++++++++++++++-- .../exchange_rates/source/coin_gecko_test.exs | 37 +++++++++++++++++++ docs/env-variables.md | 1 - 3 files changed, 66 insertions(+), 4 deletions(-) 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..5a6199acb2 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 + {:errpr, :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..3072099cbc 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,26 @@ 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" + } + ] + """ + describe "format_data/1" do setup do bypass = Bypass.open() @@ -62,4 +82,21 @@ 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}") + + {:ok, bypass: bypass} + end + + test "fetches coin id", %{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 + end end diff --git a/docs/env-variables.md b/docs/env-variables.md index dd01cbe26f..39f90f6336 100644 --- a/docs/env-variables.md +++ b/docs/env-variables.md @@ -65,4 +65,3 @@ $ 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) | master | | | -| `COIN_GECKO_ID` | | CoinGecko coin id required for fetching an exchange rate | poa-network | master | | | From 39c01bc8a2b44b2fe5bedd9d6f43ad131ca8d9b3 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 3 Sep 2019 12:49:13 +0300 Subject: [PATCH 2/5] add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca60257372..7000f890fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - [#2538](https://github.com/poanetwork/blockscout/pull/2538) - fetch the last not empty coin balance records ### 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 From 213395ec32cd1d8201bd849aa94399f1cc425b7f Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Tue, 3 Sep 2019 12:50:29 +0300 Subject: [PATCH 3/5] remove config entry --- apps/explorer/config/config.exs | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 10e8a66e82..9923625359 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -31,8 +31,6 @@ config :explorer, Explorer.ChainSpec.GenesisData, enabled: false, chain_spec_pat config :explorer, Explorer.Chain.Cache.BlockNumber, enabled: true -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 From b1ac8c69ef165380e2f368f9d7401cc5445e129d Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 6 Sep 2019 11:38:50 +0300 Subject: [PATCH 4/5] add test for all supported chains --- .../exchange_rates/source/coin_gecko_test.exs | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) 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 3072099cbc..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 @@ -34,6 +34,26 @@ defmodule Explorer.ExchangeRates.Source.CoinGeckoTest do "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" } ] """ @@ -88,15 +108,59 @@ defmodule Explorer.ExchangeRates.Source.CoinGeckoTest 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 coin id", %{bypass: bypass} do + 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 From d7f7ad64cc856bbc0ff9656c4a9109a7b4963ada Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 6 Sep 2019 12:15:25 +0300 Subject: [PATCH 5/5] fix typo --- apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5a6199acb2..9d4cfba3f4 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex @@ -70,7 +70,7 @@ defmodule Explorer.ExchangeRates.Source.CoinGecko do if symbol_data do {:ok, symbol_data["id"]} else - {:errpr, :not_found} + {:error, :not_found} end {:ok, %Response{body: body, status_code: status_code}} when status_code in 400..499 ->