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  }