diff --git a/services/wallet/thirdparty/coingecko/client.go b/services/wallet/thirdparty/coingecko/client.go index 63af51d1b..d391b5c66 100644 --- a/services/wallet/thirdparty/coingecko/client.go +++ b/services/wallet/thirdparty/coingecko/client.go @@ -8,6 +8,8 @@ import ( "strings" "sync" + "golang.org/x/exp/maps" + "github.com/status-im/status-go/services/wallet/thirdparty" "github.com/status-im/status-go/services/wallet/thirdparty/utils" ) @@ -18,10 +20,8 @@ var coinGeckoMapping = map[string]string{ "ETH": "ethereum", "AST": "airswap", "ABT": "arcblock", - "ATM": "", "BNB": "binancecoin", "BLT": "bloom", - "CDT": "", "COMP": "compound-coin", "EDG": "edgeless", "ENG": "enigma", @@ -47,7 +47,6 @@ var coinGeckoMapping = map[string]string{ "UNI": "uniswap", "USDC": "usd-coin", "USDP": "paxos-standard", - "VRS": "", "USDT": "tether", "SHIB": "shiba-inu", "LINK": "chainlink", @@ -184,23 +183,21 @@ func (c *Client) getTokens() (map[string][]GeckoToken, error) { } mapTokensToSymbols(tokens, c.tokens) - return c.tokens, nil } -func (c *Client) mapSymbolsToIds(symbols []string) ([]string, error) { +func (c *Client) mapSymbolsToIds(symbols []string) (map[string]string, error) { tokens, err := c.getTokens() if err != nil { return nil, err } - ids := make([]string, 0) - for _, symbol := range utils.RenameSymbols(symbols) { - id, err := getIDFromSymbol(tokens, symbol) + ids := make(map[string]string, 0) + for _, symbol := range symbols { + id, err := getIDFromSymbol(tokens, utils.GetRealSymbol(symbol)) if err == nil { - ids = append(ids, id) + ids[symbol] = id } } - ids = utils.RemoveDuplicates(ids) return ids, nil } @@ -212,7 +209,7 @@ func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string] } params := url.Values{} - params.Add("ids", strings.Join(ids, ",")) + params.Add("ids", strings.Join(maps.Values(ids), ",")) params.Add("vs_currencies", strings.Join(currencies, ",")) url := fmt.Sprintf("%ssimple/price", baseURL) @@ -227,17 +224,9 @@ func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string] return nil, fmt.Errorf("%s - %s", err, string(response)) } - tokens, err := c.getTokens() - if err != nil { - return nil, err - } result := make(map[string]map[string]float64) - for _, symbol := range symbols { + for symbol, id := range ids { result[symbol] = map[string]float64{} - id, err := getIDFromSymbol(tokens, utils.GetRealSymbol(symbol)) - if err != nil { - return nil, err - } for _, currency := range currencies { result[symbol][currency] = prices[id][strings.ToLower(currency)] } @@ -272,7 +261,7 @@ func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[ } params := url.Values{} - params.Add("ids", strings.Join(ids, ",")) + params.Add("ids", strings.Join(maps.Values(ids), ",")) params.Add("vs_currency", currency) params.Add("order", "market_cap_desc") params.Add("per_page", "250") @@ -292,17 +281,8 @@ func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[ return nil, fmt.Errorf("%s - %s", err, string(response)) } - tokens, err := c.getTokens() - if err != nil { - return nil, err - } - result := make(map[string]thirdparty.TokenMarketValues) - for _, symbol := range symbols { - id, err := getIDFromSymbol(tokens, utils.GetRealSymbol(symbol)) - if err != nil { - return nil, err - } + for symbol, id := range ids { for _, marketValue := range marketValues { if id != marketValue.ID { continue diff --git a/services/wallet/thirdparty/coingecko/client_test.go b/services/wallet/thirdparty/coingecko/client_test.go index 894288298..29480814f 100644 --- a/services/wallet/thirdparty/coingecko/client_test.go +++ b/services/wallet/thirdparty/coingecko/client_test.go @@ -7,9 +7,9 @@ import ( "reflect" "testing" - "github.com/status-im/status-go/services/wallet/thirdparty" - "github.com/stretchr/testify/require" + + "github.com/status-im/status-go/services/wallet/thirdparty" ) type TestTokenPlatform struct { @@ -53,12 +53,12 @@ func TestGetTokensSuccess(t *testing.T) { } expectedMap := map[string][]GeckoToken{ - "ETH": []GeckoToken{{ + "ETH": {{ ID: "ethereum", Symbol: "eth", Name: "Ethereum", }}, - "SNT": []GeckoToken{{ + "SNT": {{ ID: "status", Symbol: "snt", Name: "Status", @@ -176,3 +176,67 @@ func TestGetTokensFailure(t *testing.T) { _, err := geckoClient.getTokens() require.Error(t, err) } + +func TestFetchPrices(t *testing.T) { + mux := http.NewServeMux() + + // Register handlers for different URL paths + mux.HandleFunc("/coins/list", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + response := "[{\"id\":\"ethereum\",\"symbol\":\"eth\",\"name\":\"Ethereum\",\"platforms\":{\"ethereum\":\"0x5e21d1ee5cf0077b314c381720273ae82378d613\"}},{\"id\":\"status\",\"symbol\":\"snt\",\"name\":\"Status\",\"platforms\":{\"ethereum\":\"0x78ba134c3ace18e69837b01703d07f0db6fb0a60\"}}]" + _, _ = w.Write([]byte(response)) + }) + + mux.HandleFunc("/simple/price", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + response := "{\"ethereum\":{\"usd\":3181.32},\"status\":{\"usd\":0.02391704}}" + _, _ = w.Write([]byte(response)) + }) + + srv := httptest.NewServer(mux) + + geckoClient := &Client{ + httpClient: thirdparty.NewHTTPClient(), + tokens: make(map[string][]GeckoToken), + tokensURL: srv.URL + "/coins/list", + } + + symbols := []string{"ETH", "SNT", "UNSUPPORTED", "TOKENS"} + prices, err := geckoClient.FetchPrices(symbols, []string{"USD"}) + require.NoError(t, err) + require.Len(t, prices, 2) +} + +func TestFetchMarketValues(t *testing.T) { + mux := http.NewServeMux() + + // Register handlers for different URL paths + mux.HandleFunc("/coins/list", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + response := "[{\"id\":\"ethereum\",\"symbol\":\"eth\",\"name\":\"Ethereum\",\"platforms\":{\"ethereum\":\"0x5e21d1ee5cf0077b314c381720273ae82378d613\"}},{\"id\":\"status\",\"symbol\":\"snt\",\"name\":\"Status\",\"platforms\":{\"ethereum\":\"0x78ba134c3ace18e69837b01703d07f0db6fb0a60\"}}]" + _, _ = w.Write([]byte(response)) + }) + + mux.HandleFunc("/coins/markets", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + response := "[{\"id\":\"ethereum\",\"symbol\":\"eth\",\"name\":\"Ethereum\",\"image\":\"https://coin-images.coingecko.com/coins/images/279/large/ethereum.png?1696501628\",\"current_price\":3177.16,\"market_cap\":382035912506,\"market_cap_rank\":2,\"fully_diluted_valuation\":382035912506,\"total_volume\":18958367285,\"high_24h\":3325.57,\"low_24h\":3139.38,\"price_change_24h\":-146.70781392198978,\"price_change_percentage_24h\":-4.41377,\"market_cap_change_24h\":-17315836985.42914,\"market_cap_change_percentage_24h\":-4.33599,\"circulating_supply\":120251313.934882,\"total_supply\":120251313.934882,\"max_supply\":null,\"ath\":4878.26,\"ath_change_percentage\":-34.74074,\"ath_date\":\"2021-11-10T14:24:19.604Z\",\"atl\":0.432979,\"atl_change_percentage\":735159.10684,\"atl_date\":\"2015-10-20T00:00:00.000Z\",\"roi\":{\"times\":64.75457822761112,\"currency\":\"btc\",\"percentage\":6475.457822761112},\"last_updated\":\"2024-08-01T14:17:02.604Z\",\"price_change_percentage_1h_in_currency\":-0.14302683386053758,\"price_change_percentage_24h_in_currency\":-4.413773698570276},{\"id\":\"status\",\"symbol\":\"snt\",\"name\":\"Status\",\"image\":\"https://coin-images.coingecko.com/coins/images/779/large/status.png?1696501931\",\"current_price\":0.02387956,\"market_cap\":94492012,\"market_cap_rank\":420,\"fully_diluted_valuation\":162355386,\"total_volume\":3315607,\"high_24h\":0.02528227,\"low_24h\":0.02351923,\"price_change_24h\":-0.001177587387552543,\"price_change_percentage_24h\":-4.69961,\"market_cap_change_24h\":-5410268.579258412,\"market_cap_change_percentage_24h\":-5.41556,\"circulating_supply\":3960483788.3096976,\"total_supply\":6804870174.0,\"max_supply\":null,\"ath\":0.684918,\"ath_change_percentage\":-96.50467,\"ath_date\":\"2018-01-03T00:00:00.000Z\",\"atl\":0.00592935,\"atl_change_percentage\":303.75704,\"atl_date\":\"2020-03-13T02:10:36.877Z\",\"roi\":null,\"last_updated\":\"2024-08-01T14:16:20.805Z\",\"price_change_percentage_1h_in_currency\":-0.21239208982552796,\"price_change_percentage_24h_in_currency\":-4.699606730698922}]" + _, _ = w.Write([]byte(response)) + }) + + srv := httptest.NewServer(mux) + + geckoClient := &Client{ + httpClient: thirdparty.NewHTTPClient(), + tokens: make(map[string][]GeckoToken), + tokensURL: srv.URL + "/coins/list", + } + + symbols := []string{"ETH", "SNT", "UNSUPPORTED", "TOKENS"} + prices, err := geckoClient.FetchTokenMarketValues(symbols, "USD") + require.NoError(t, err) + require.Len(t, prices, 2) +}