fix_: Prioritize tokens on eth platform (#5151)
This commit is contained in:
parent
3b5eab3bf1
commit
838311e902
|
@ -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",
|
||||||
|
@ -80,20 +76,52 @@ type GeckoMarketValues struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GeckoToken struct {
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue