mirror of
https://github.com/status-im/status-go.git
synced 2025-03-03 16:11:05 +00:00
fix(wallet/coingecko): Fix coingecko crash on race writing to tokens map (#3274)
This commit is contained in:
parent
804b5b43b4
commit
2c9714f0f8
36
services/wallet/thirdparty/coingecko/client.go
vendored
36
services/wallet/thirdparty/coingecko/client.go
vendored
@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
@ -79,12 +80,14 @@ type GeckoToken struct {
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
client *http.Client
|
||||
tokens map[string]GeckoToken
|
||||
client *http.Client
|
||||
tokens map[string]GeckoToken
|
||||
tokensURL string
|
||||
fetchTokensMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewClient() *Client {
|
||||
return &Client{client: &http.Client{Timeout: time.Minute}, tokens: make(map[string]GeckoToken)}
|
||||
return &Client{client: &http.Client{Timeout: time.Minute}, tokens: make(map[string]GeckoToken), tokensURL: fmt.Sprintf("%scoins/list", baseURL)}
|
||||
}
|
||||
|
||||
func (c *Client) DoQuery(url string) (*http.Response, error) {
|
||||
@ -96,13 +99,27 @@ func (c *Client) DoQuery(url string) (*http.Response, error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func mapTokensToSymbols(tokens []GeckoToken, tokenMap map[string]GeckoToken) {
|
||||
for _, token := range tokens {
|
||||
if id, ok := coinGeckoMapping[strings.ToUpper(token.Symbol)]; ok {
|
||||
if id != token.ID {
|
||||
continue
|
||||
}
|
||||
}
|
||||
tokenMap[strings.ToUpper(token.Symbol)] = token
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) getTokens() (map[string]GeckoToken, error) {
|
||||
|
||||
c.fetchTokensMutex.Lock()
|
||||
defer c.fetchTokensMutex.Unlock()
|
||||
|
||||
if len(c.tokens) > 0 {
|
||||
return c.tokens, nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%scoins/list", baseURL)
|
||||
resp, err := c.DoQuery(url)
|
||||
resp, err := c.DoQuery(c.tokensURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -119,14 +136,7 @@ func (c *Client) getTokens() (map[string]GeckoToken, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, token := range tokens {
|
||||
if id, ok := coinGeckoMapping[strings.ToUpper(token.Symbol)]; ok {
|
||||
if id != token.ID {
|
||||
continue
|
||||
}
|
||||
}
|
||||
c.tokens[strings.ToUpper(token.Symbol)] = token
|
||||
}
|
||||
mapTokensToSymbols(tokens, c.tokens)
|
||||
|
||||
return c.tokens, nil
|
||||
}
|
||||
|
82
services/wallet/thirdparty/coingecko/client_test.go
vendored
Normal file
82
services/wallet/thirdparty/coingecko/client_test.go
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package coingecko
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, response []byte) (*httptest.Server, func()) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
_, err := w.Write(response)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
|
||||
return srv, func() {
|
||||
srv.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTokensSuccess(t *testing.T) {
|
||||
expected := []GeckoToken{
|
||||
{
|
||||
ID: "ethereum",
|
||||
Symbol: "eth",
|
||||
Name: "Ethereum",
|
||||
},
|
||||
{
|
||||
ID: "status",
|
||||
Symbol: "snt",
|
||||
Name: "Status",
|
||||
},
|
||||
}
|
||||
|
||||
expectedMap := map[string]GeckoToken{
|
||||
"ETH": {
|
||||
ID: "ethereum",
|
||||
Symbol: "eth",
|
||||
Name: "Ethereum",
|
||||
},
|
||||
"SNT": {
|
||||
ID: "status",
|
||||
Symbol: "snt",
|
||||
Name: "Status",
|
||||
},
|
||||
}
|
||||
response, _ := json.Marshal(expected)
|
||||
|
||||
srv, stop := setupTest(t, response)
|
||||
defer stop()
|
||||
|
||||
geckoClient := &Client{
|
||||
client: srv.Client(),
|
||||
tokens: make(map[string]GeckoToken),
|
||||
tokensURL: srv.URL,
|
||||
}
|
||||
|
||||
tokenMap, err := geckoClient.getTokens()
|
||||
require.NoError(t, err)
|
||||
require.True(t, reflect.DeepEqual(expectedMap, tokenMap))
|
||||
}
|
||||
|
||||
func TestGetTokensFailure(t *testing.T) {
|
||||
resp := []byte{}
|
||||
srv, stop := setupTest(t, resp)
|
||||
defer stop()
|
||||
|
||||
geckoClient := &Client{
|
||||
client: srv.Client(),
|
||||
tokens: make(map[string]GeckoToken),
|
||||
tokensURL: srv.URL,
|
||||
}
|
||||
|
||||
_, err := geckoClient.getTokens()
|
||||
require.Error(t, err)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user