package priceupdater import ( "context" "fmt" "net/http" "time" "github.com/dghubble/sling" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/hermeznetwork/hermez-node/log" "github.com/hermeznetwork/tracerr" ) const ( defaultMaxIdleConns = 10 defaultIdleConnTimeout = 2 * time.Second ) // APIType defines the token exchange API type APIType string const ( // APITypeBitFinexV2 is the http API used by bitfinex V2 APITypeBitFinexV2 APIType = "bitfinexV2" ) func (t *APIType) valid() bool { switch *t { case APITypeBitFinexV2: return true default: return false } } // PriceUpdater definition type PriceUpdater struct { db *historydb.HistoryDB apiURL string apiType APIType tokenSymbols []string } // NewPriceUpdater is the constructor for the updater func NewPriceUpdater(apiURL string, apiType APIType, db *historydb.HistoryDB) (*PriceUpdater, error) { tokenSymbols := []string{} if !apiType.valid() { return nil, tracerr.Wrap(fmt.Errorf("Invalid apiType: %v", apiType)) } return &PriceUpdater{ db: db, apiURL: apiURL, apiType: apiType, tokenSymbols: tokenSymbols, }, nil } func getTokenPriceBitfinex(ctx context.Context, client *sling.Sling, tokenSymbol string) (float64, error) { state := [10]float64{} req, err := client.New().Get("ticker/t" + tokenSymbol + "USD").Request() if err != nil { return 0, tracerr.Wrap(err) } res, err := client.Do(req.WithContext(ctx), &state, nil) if err != nil { return 0, tracerr.Wrap(err) } if res.StatusCode != http.StatusOK { return 0, tracerr.Wrap(fmt.Errorf("http response is not is %v", res.StatusCode)) } return state[6], nil } // UpdatePrices is triggered by the Coordinator, and internally will update the token prices in the db func (p *PriceUpdater) UpdatePrices(ctx context.Context) { tr := &http.Transport{ MaxIdleConns: defaultMaxIdleConns, IdleConnTimeout: defaultIdleConnTimeout, DisableCompression: true, } httpClient := &http.Client{Transport: tr} client := sling.New().Base(p.apiURL).Client(httpClient) for _, tokenSymbol := range p.tokenSymbols { var tokenPrice float64 var err error switch p.apiType { case APITypeBitFinexV2: tokenPrice, err = getTokenPriceBitfinex(ctx, client, tokenSymbol) } if ctx.Err() != nil { return } if err != nil { log.Errorw("token price not updated (get error)", "err", err, "token", tokenSymbol, "apiType", p.apiType) } if err = p.db.UpdateTokenValue(tokenSymbol, tokenPrice); err != nil { log.Errorw("token price not updated (db error)", "err", err, "token", tokenSymbol, "apiType", p.apiType) } } } // UpdateTokenList get the registered token symbols from HistoryDB func (p *PriceUpdater) UpdateTokenList() error { tokenSymbols, err := p.db.GetTokenSymbols() if err != nil { return tracerr.Wrap(err) } p.tokenSymbols = tokenSymbols return nil }