decred.org/dcrdex@v1.0.3/dex/fiatrates/fiatrates.go (about) 1 package fiatrates 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "time" 8 9 "decred.org/dcrdex/dex" 10 "decred.org/dcrdex/dex/dexnet" 11 ) 12 13 const ( 14 coinpaprikaURL = "https://api.coinpaprika.com/v1/tickers" 15 fiatRequestTimeout = time.Second * 5 16 ) 17 18 func CoinpapSlug(name, symbol string) string { 19 name, symbol = parseCoinpapNameSymbol(name, symbol) 20 slug := fmt.Sprintf("%s-%s", symbol, name) 21 // Special handling for asset names with multiple space, e.g Bitcoin Cash. 22 return strings.ToLower(strings.ReplaceAll(slug, " ", "-")) 23 } 24 25 type CoinpaprikaAsset struct { 26 AssetID uint32 27 Name string 28 Symbol string 29 } 30 31 func parseCoinpapNameSymbol(name, symbol string) (string, string) { 32 parts := strings.Split(symbol, ".") 33 network := symbol 34 if len(parts) == 2 { 35 symbol, network = parts[0], parts[1] 36 } 37 switch symbol { 38 case "usdc": 39 name = "usd-coin" 40 case "polygon": 41 symbol = "matic" 42 name = "polygon" 43 case "weth": 44 name = "weth" 45 case "matic": 46 switch network { 47 case "eth": 48 symbol, name = "matic", "polygon" 49 } 50 } 51 return name, symbol 52 } 53 54 // FetchCoinpaprikaRates retrieves and parses fiat rate data from the 55 // Coinpaprika API. See https://api.coinpaprika.com/#operation/getTickersById 56 // for sample request and response information. 57 func FetchCoinpaprikaRates(ctx context.Context, assets []*CoinpaprikaAsset, log dex.Logger) map[uint32]float64 { 58 fiatRates := make(map[uint32]float64) 59 slugAssets := make(map[string][]uint32) 60 for _, a := range assets { 61 slug := CoinpapSlug(a.Name, a.Symbol) 62 slugAssets[slug] = append(slugAssets[slug], a.AssetID) 63 } 64 65 ctx, cancel := context.WithTimeout(ctx, fiatRequestTimeout) 66 defer cancel() 67 68 var res []*struct { 69 ID string `json:"id"` 70 Quotes struct { 71 USD struct { 72 Price float64 `json:"price"` 73 } `json:"USD"` 74 } `json:"quotes"` 75 } 76 77 if err := getRates(ctx, coinpaprikaURL, &res); err != nil { 78 log.Errorf("Error getting fiat exchange rates from coinpaprika: %v", err) 79 return fiatRates 80 } 81 for _, coinInfo := range res { 82 assetIDs, found := slugAssets[coinInfo.ID] 83 if !found { 84 continue 85 } 86 87 price := coinInfo.Quotes.USD.Price 88 if price == 0 { 89 log.Errorf("zero-price returned from coinpaprika for slug %s", coinInfo.ID) 90 continue 91 } 92 for _, assetID := range assetIDs { 93 fiatRates[assetID] = price 94 } 95 } 96 return fiatRates 97 } 98 99 func getRates(ctx context.Context, uri string, thing any) error { 100 return dexnet.Get(ctx, uri, thing, dexnet.WithSizeLimit(1<<22)) 101 }