fix_: Prioritize tokens on eth platform (#5151)

This commit is contained in:
Cuteivist 2024-05-15 07:33:12 +02:00 committed by GitHub
parent 3b5eab3bf1
commit 838311e902
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 189 additions and 38 deletions

View File

@ -18,7 +18,6 @@ var coinGeckoMapping = map[string]string{
"SNT": "status", "SNT": "status",
"ETH": "ethereum", "ETH": "ethereum",
"AST": "airswap", "AST": "airswap",
"AMB": "",
"ABT": "arcblock", "ABT": "arcblock",
"ATM": "", "ATM": "",
"BNB": "binancecoin", "BNB": "binancecoin",
@ -26,7 +25,6 @@ var coinGeckoMapping = map[string]string{
"CDT": "", "CDT": "",
"COMP": "compound-coin", "COMP": "compound-coin",
"EDG": "edgeless", "EDG": "edgeless",
"ELF": "",
"ENG": "enigma", "ENG": "enigma",
"EOS": "eos", "EOS": "eos",
"GEN": "daostack", "GEN": "daostack",
@ -46,13 +44,11 @@ var coinGeckoMapping = map[string]string{
"GRT": "the-graph", "GRT": "the-graph",
"TNT": "tierion", "TNT": "tierion",
"TRX": "tron", "TRX": "tron",
"TGT": "",
"RARE": "superrare", "RARE": "superrare",
"UNI": "uniswap", "UNI": "uniswap",
"USDC": "usd-coin", "USDC": "usd-coin",
"USDP": "paxos-standard", "USDP": "paxos-standard",
"VRS": "", "VRS": "",
"TIME": "",
"USDT": "tether", "USDT": "tether",
"SHIB": "shiba-inu", "SHIB": "shiba-inu",
"LINK": "chainlink", "LINK": "chainlink",
@ -83,17 +79,49 @@ type GeckoToken struct {
ID string `json:"id"` ID string `json:"id"`
Symbol string `json:"symbol"` Symbol string `json:"symbol"`
Name string `json:"name"` Name string `json:"name"`
EthPlatform bool
} }
type Client struct { type Client struct {
client *http.Client client *http.Client
tokens map[string]GeckoToken tokens map[string][]GeckoToken
tokensURL string tokensURL string
fetchTokensMutex sync.Mutex fetchTokensMutex sync.Mutex
} }
func NewClient() *Client { func NewClient() *Client {
return &Client{client: &http.Client{Timeout: time.Minute}, tokens: make(map[string]GeckoToken), tokensURL: fmt.Sprintf("%scoins/list", baseURL)} return &Client{client: &http.Client{Timeout: time.Minute}, tokens: make(map[string][]GeckoToken), tokensURL: fmt.Sprintf("%scoins/list?include_platform=true", baseURL)}
}
func (gt *GeckoToken) UnmarshalJSON(data []byte) error {
// Define an auxiliary struct to hold the JSON data
var aux struct {
ID string `json:"id"`
Symbol string `json:"symbol"`
Name string `json:"name"`
Platforms struct {
Ethereum string `json:"ethereum"`
// Other platforms can be added here if needed
} `json:"platforms"`
}
// Unmarshal the JSON data into the auxiliary struct
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
// Set the fields of GeckoToken from the auxiliary struct
gt.ID = aux.ID
gt.Symbol = aux.Symbol
gt.Name = aux.Name
// Check if "ethereum" key exists in the platforms map
if aux.Platforms.Ethereum != "" {
gt.EthPlatform = true
} else {
gt.EthPlatform = false
}
return nil
} }
func (c *Client) DoQuery(url string) (*http.Response, error) { func (c *Client) DoQuery(url string) (*http.Response, error) {
@ -105,18 +133,40 @@ func (c *Client) DoQuery(url string) (*http.Response, error) {
return resp, nil return resp, nil
} }
func mapTokensToSymbols(tokens []GeckoToken, tokenMap map[string]GeckoToken) { func mapTokensToSymbols(tokens []GeckoToken, tokenMap map[string][]GeckoToken) {
for _, token := range tokens { for _, token := range tokens {
if id, ok := coinGeckoMapping[strings.ToUpper(token.Symbol)]; ok { symbol := strings.ToUpper(token.Symbol)
if id, ok := coinGeckoMapping[symbol]; ok {
if id != token.ID { if id != token.ID {
continue continue
} }
} }
tokenMap[strings.ToUpper(token.Symbol)] = token tokenMap[symbol] = append(tokenMap[symbol], token)
} }
} }
func (c *Client) getTokens() (map[string]GeckoToken, error) { func getGeckoTokenFromSymbol(tokens map[string][]GeckoToken, symbol string) (GeckoToken, error) {
tokenList, ok := tokens[strings.ToUpper(symbol)]
if !ok {
return GeckoToken{}, fmt.Errorf("token not found for symbol %s", symbol)
}
for _, t := range tokenList {
if t.EthPlatform {
return t, nil
}
}
return tokenList[0], nil
}
func getIDFromSymbol(tokens map[string][]GeckoToken, symbol string) (string, error) {
token, err := getGeckoTokenFromSymbol(tokens, symbol)
if err != nil {
return "", err
}
return token.ID, nil
}
func (c *Client) getTokens() (map[string][]GeckoToken, error) {
c.fetchTokensMutex.Lock() c.fetchTokensMutex.Lock()
defer c.fetchTokensMutex.Unlock() defer c.fetchTokensMutex.Unlock()
@ -153,23 +203,16 @@ func (c *Client) mapSymbolsToIds(symbols []string) ([]string, error) {
} }
ids := make([]string, 0) ids := make([]string, 0)
for _, symbol := range utils.RenameSymbols(symbols) { for _, symbol := range utils.RenameSymbols(symbols) {
if token, ok := tokens[symbol]; ok { id, err := getIDFromSymbol(tokens, symbol)
ids = append(ids, token.ID) if err == nil {
ids = append(ids, id)
} }
} }
ids = utils.RemoveDuplicates(ids) ids = utils.RemoveDuplicates(ids)
return ids, nil return ids, nil
} }
func (c *Client) getIDFromSymbol(symbol string) (string, error) {
tokens, err := c.getTokens()
if err != nil {
return "", err
}
return tokens[strings.ToUpper(symbol)].ID, nil
}
func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) { func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) {
ids, err := c.mapSymbolsToIds(symbols) ids, err := c.mapSymbolsToIds(symbols)
if err != nil { if err != nil {
@ -193,10 +236,14 @@ func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]
return nil, fmt.Errorf("%s - %s", err, string(body)) return nil, fmt.Errorf("%s - %s", err, string(body))
} }
tokens, err := c.getTokens()
if err != nil {
return nil, err
}
result := make(map[string]map[string]float64) result := make(map[string]map[string]float64)
for _, symbol := range symbols { for _, symbol := range symbols {
result[symbol] = map[string]float64{} result[symbol] = map[string]float64{}
id, err := c.getIDFromSymbol(utils.GetRealSymbol(symbol)) id, err := getIDFromSymbol(tokens, utils.GetRealSymbol(symbol))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -215,14 +262,14 @@ func (c *Client) FetchTokenDetails(symbols []string) (map[string]thirdparty.Toke
} }
result := make(map[string]thirdparty.TokenDetails) result := make(map[string]thirdparty.TokenDetails)
for _, symbol := range symbols { for _, symbol := range symbols {
if value, ok := tokens[utils.GetRealSymbol(symbol)]; ok { token, err := getGeckoTokenFromSymbol(tokens, utils.GetRealSymbol(symbol))
if err == nil {
result[symbol] = thirdparty.TokenDetails{ result[symbol] = thirdparty.TokenDetails{
ID: value.ID, ID: token.ID,
Name: value.Name, Name: token.Name,
Symbol: symbol, Symbol: symbol,
} }
} }
} }
return result, nil return result, nil
} }
@ -251,9 +298,14 @@ func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[
return nil, fmt.Errorf("%s - %s", err, string(body)) return nil, fmt.Errorf("%s - %s", err, string(body))
} }
tokens, err := c.getTokens()
if err != nil {
return nil, err
}
result := make(map[string]thirdparty.TokenMarketValues) result := make(map[string]thirdparty.TokenMarketValues)
for _, symbol := range symbols { for _, symbol := range symbols {
id, err := c.getIDFromSymbol(utils.GetRealSymbol(symbol)) id, err := getIDFromSymbol(tokens, utils.GetRealSymbol(symbol))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -282,7 +334,12 @@ func (c *Client) FetchHistoricalHourlyPrices(symbol string, currency string, lim
} }
func (c *Client) FetchHistoricalDailyPrices(symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.HistoricalPrice, error) { func (c *Client) FetchHistoricalDailyPrices(symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.HistoricalPrice, error) {
id, err := c.getIDFromSymbol(utils.GetRealSymbol(symbol)) tokens, err := c.getTokens()
if err != nil {
return nil, err
}
id, err := getIDFromSymbol(tokens, utils.GetRealSymbol(symbol))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -10,6 +10,18 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type TestTokenPlatform struct {
Ethereum string `json:"ethereum"`
Arb string `json:"arb"`
}
type TestGeckoToken struct {
ID string `json:"id"`
Symbol string `json:"symbol"`
Name string `json:"name"`
Platforms TestTokenPlatform `json:"platforms"`
}
func setupTest(t *testing.T, response []byte) (*httptest.Server, func()) { func setupTest(t *testing.T, response []byte) (*httptest.Server, func()) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200) w.WriteHeader(200)
@ -38,17 +50,17 @@ func TestGetTokensSuccess(t *testing.T) {
}, },
} }
expectedMap := map[string]GeckoToken{ expectedMap := map[string][]GeckoToken{
"ETH": { "ETH": []GeckoToken{{
ID: "ethereum", ID: "ethereum",
Symbol: "eth", Symbol: "eth",
Name: "Ethereum", Name: "Ethereum",
}, }},
"SNT": { "SNT": []GeckoToken{{
ID: "status", ID: "status",
Symbol: "snt", Symbol: "snt",
Name: "Status", Name: "Status",
}, }},
} }
response, _ := json.Marshal(expected) response, _ := json.Marshal(expected)
@ -57,7 +69,89 @@ func TestGetTokensSuccess(t *testing.T) {
geckoClient := &Client{ geckoClient := &Client{
client: srv.Client(), client: srv.Client(),
tokens: make(map[string]GeckoToken), tokens: make(map[string][]GeckoToken),
tokensURL: srv.URL,
}
tokenMap, err := geckoClient.getTokens()
require.NoError(t, err)
require.True(t, reflect.DeepEqual(expectedMap, tokenMap))
}
func TestGetTokensEthPlatform(t *testing.T) {
tokenList := []TestGeckoToken{
{
ID: "ethereum",
Symbol: "eth-test",
Name: "Ethereum",
Platforms: TestTokenPlatform{
Ethereum: "0x123",
},
},
{
ID: "usdt-bridge-test",
Symbol: "usdt-test",
Name: "USDT Bridge Test",
Platforms: TestTokenPlatform{
Arb: "0x123",
},
},
{
ID: "tether",
Symbol: "usdt-test",
Name: "Tether",
Platforms: TestTokenPlatform{
Arb: "0x1234",
Ethereum: "0x12345",
},
},
{
ID: "AirDao",
Symbol: "amb-test",
Name: "Amber",
Platforms: TestTokenPlatform{
Arb: "0x123455",
},
},
}
expectedMap := map[string][]GeckoToken{
"ETH-TEST": {{
ID: "ethereum",
Symbol: "eth-test",
Name: "Ethereum",
EthPlatform: true,
}},
"USDT-TEST": {
{
ID: "usdt-bridge-test",
Symbol: "usdt-test",
Name: "USDT Bridge Test",
EthPlatform: false,
},
{
ID: "tether",
Symbol: "usdt-test",
Name: "Tether",
EthPlatform: true,
},
},
"AMB-TEST": {{
ID: "AirDao",
Symbol: "amb-test",
Name: "Amber",
EthPlatform: false,
}},
}
response, _ := json.Marshal(tokenList)
srv, stop := setupTest(t, response)
defer stop()
geckoClient := &Client{
client: srv.Client(),
tokens: make(map[string][]GeckoToken),
tokensURL: srv.URL, tokensURL: srv.URL,
} }
@ -73,7 +167,7 @@ func TestGetTokensFailure(t *testing.T) {
geckoClient := &Client{ geckoClient := &Client{
client: srv.Client(), client: srv.Client(),
tokens: make(map[string]GeckoToken), tokens: make(map[string][]GeckoToken),
tokensURL: srv.URL, tokensURL: srv.URL,
} }