github.com/NpoolPlatform/chain-middleware@v0.0.0-20240228100535-eb1bcf896eb9/pkg/currency/coinbase/coinbase.go (about) 1 package coinbase 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "strings" 8 "time" 9 10 "github.com/NpoolPlatform/go-service-framework/pkg/logger" 11 12 "github.com/shopspring/decimal" 13 14 "github.com/go-resty/resty/v2" 15 ) 16 17 const ( 18 coinAPI = "https://api.coinbase.com/v2/prices/COIN-USD/sell" 19 fiatAPI = "https://api.coinbase.com/v2/exchange-rates?currency=USD" 20 timeout = 5 21 ) 22 23 type coinData struct { 24 Base string `json:"base"` 25 Currency string `json:"currency"` 26 Amount string `json:"amount"` 27 } 28 29 type coinResp struct { 30 Data coinData `json:"data"` 31 } 32 33 func CoinBaseUSDPrice(coinName string) (decimal.Decimal, error) { 34 socksProxy := os.Getenv("ENV_CURRENCY_REQUEST_PROXY") 35 url := strings.ReplaceAll(coinAPI, "COIN", coinName) 36 37 cli := resty.New() 38 cli = cli.SetTimeout(timeout * time.Second) 39 if socksProxy != "" { 40 cli = cli.SetProxy(socksProxy) 41 } 42 43 resp, err := cli.R().Get(url) 44 if err != nil { 45 logger.Sugar().Errorw( 46 "CoinBaseUSDPrice", 47 "URL", url, 48 "CoinName", coinName, 49 "Proxy", socksProxy, 50 "error", err, 51 ) 52 return decimal.Decimal{}, err 53 } 54 r := coinResp{} 55 err = json.Unmarshal(resp.Body(), &r) 56 if err != nil { 57 logger.Sugar().Errorw( 58 "CoinBaseUSDPrice", 59 "URL", url, 60 "CoinName", coinName, 61 "Proxy", socksProxy, 62 "Resp", string(resp.Body()), 63 "error", err, 64 ) 65 return decimal.Decimal{}, err 66 } 67 68 if coinName != r.Data.Base { 69 logger.Sugar().Errorw( 70 "CoinBaseUSDPrice", 71 "URL", url, 72 "CoinName", coinName, 73 "Proxy", socksProxy, 74 "Resp", string(resp.Body()), 75 "error", err, 76 ) 77 return decimal.Decimal{}, fmt.Errorf("invalid coin currency") 78 } 79 80 amount, err := decimal.NewFromString(r.Data.Amount) 81 if err != nil { 82 logger.Sugar().Errorw( 83 "CoinBaseUSDPrice", 84 "URL", url, 85 "CoinName", coinName, 86 "Proxy", socksProxy, 87 "Resp", string(resp.Body()), 88 "error", err, 89 ) 90 return decimal.Decimal{}, err 91 } 92 93 return amount, nil 94 } 95 96 func CoinBaseUSDPrices(coinNames []string) (map[string]decimal.Decimal, error) { 97 prices := map[string]decimal.Decimal{} 98 99 for _, name := range coinNames { 100 price, err := CoinBaseUSDPrice(name) 101 if err != nil { 102 logger.Sugar().Errorw( 103 "CoinBaseUSDPrices", 104 "CoinName", name, 105 "error", err, 106 ) 107 return nil, err 108 } 109 prices[name] = price 110 111 time.Sleep(500 * time.Millisecond) //nolint 112 } 113 114 if len(prices) == 0 { 115 return nil, fmt.Errorf("invalid coins") 116 } 117 118 return prices, nil 119 } 120 121 type fiatData struct { 122 Base string `json:"base"` 123 Rates map[string]string `json:"rates"` 124 } 125 126 type fiatResp struct { 127 Data fiatData `json:"data"` 128 } 129 130 func CoinBaseFiatPrices(fiatNames []string) (map[string]decimal.Decimal, error) { 131 socksProxy := os.Getenv("ENV_CURRENCY_REQUEST_PROXY") 132 133 cli := resty.New() 134 cli = cli.SetTimeout(timeout * time.Second) 135 if socksProxy != "" { 136 cli = cli.SetProxy(socksProxy) 137 } 138 139 resp, err := cli.R().Get(fiatAPI) 140 if err != nil { 141 logger.Sugar().Errorw( 142 "CoinBaseFiatCurrency", 143 "URL", fiatAPI, 144 "Proxy", socksProxy, 145 "error", err, 146 ) 147 return nil, err 148 } 149 r := fiatResp{} 150 err = json.Unmarshal(resp.Body(), &r) 151 if err != nil { 152 logger.Sugar().Errorw( 153 "CoinBaseFiatCurrency", 154 "URL", fiatAPI, 155 "Proxy", socksProxy, 156 "Resp", string(resp.Body()), 157 "error", err, 158 ) 159 return nil, err 160 } 161 162 respMap := map[string]decimal.Decimal{} 163 for k, v := range r.Data.Rates { 164 c, err := decimal.NewFromString(v) 165 if err != nil { 166 logger.Sugar().Errorw( 167 "CoinBaseFiatCurrency", 168 "URL", fiatAPI, 169 "Proxy", socksProxy, 170 "Resp", string(resp.Body()), 171 "error", err, 172 ) 173 return nil, err 174 } 175 respMap[k] = c 176 } 177 return respMap, nil 178 }