arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/priceupdater/priceupdater.go
priceupdater/priceupdater.goRaw
  1package priceupdater
  2
  3import (
  4	"context"
  5	"fmt"
  6	"net/http"
  7	"time"
  8
  9	"github.com/dghubble/sling"
 10	"github.com/hermeznetwork/hermez-node/db/historydb"
 11	"github.com/hermeznetwork/hermez-node/log"
 12	"github.com/hermeznetwork/tracerr"
 13)
 14
 15const (
 16	defaultMaxIdleConns    = 10
 17	defaultIdleConnTimeout = 2 * time.Second
 18)
 19
 20// APIType defines the token exchange API
 21type APIType string
 22
 23const (
 24	// APITypeBitFinexV2 is the http API used by bitfinex V2
 25	APITypeBitFinexV2 APIType = "bitfinexV2"
 26)
 27
 28func (t *APIType) valid() bool {
 29	switch *t {
 30	case APITypeBitFinexV2:
 31		return true
 32	default:
 33		return false
 34	}
 35}
 36
 37// PriceUpdater definition
 38type PriceUpdater struct {
 39	db           *historydb.HistoryDB
 40	apiURL       string
 41	apiType      APIType
 42	tokenSymbols []string
 43}
 44
 45// NewPriceUpdater is the constructor for the updater
 46func NewPriceUpdater(apiURL string, apiType APIType, db *historydb.HistoryDB) (*PriceUpdater,
 47	error) {
 48	tokenSymbols := []string{}
 49	if !apiType.valid() {
 50		return nil, tracerr.Wrap(fmt.Errorf("Invalid apiType: %v", apiType))
 51	}
 52	return &PriceUpdater{
 53		db:           db,
 54		apiURL:       apiURL,
 55		apiType:      apiType,
 56		tokenSymbols: tokenSymbols,
 57	}, nil
 58}
 59
 60func getTokenPriceBitfinex(ctx context.Context, client *sling.Sling,
 61	tokenSymbol string) (float64, error) {
 62	state := [10]float64{}
 63	req, err := client.New().Get("ticker/t" + tokenSymbol + "USD").Request()
 64	if err != nil {
 65		return 0, tracerr.Wrap(err)
 66	}
 67	res, err := client.Do(req.WithContext(ctx), &state, nil)
 68	if err != nil {
 69		return 0, tracerr.Wrap(err)
 70	}
 71	if res.StatusCode != http.StatusOK {
 72		return 0, tracerr.Wrap(fmt.Errorf("http response is not is %v", res.StatusCode))
 73	}
 74	return state[6], nil
 75}
 76
 77// UpdatePrices is triggered by the Coordinator, and internally will update the
 78// token prices in the db
 79func (p *PriceUpdater) UpdatePrices(ctx context.Context) {
 80	tr := &http.Transport{
 81		MaxIdleConns:       defaultMaxIdleConns,
 82		IdleConnTimeout:    defaultIdleConnTimeout,
 83		DisableCompression: true,
 84	}
 85	httpClient := &http.Client{Transport: tr}
 86	client := sling.New().Base(p.apiURL).Client(httpClient)
 87
 88	for _, tokenSymbol := range p.tokenSymbols {
 89		var tokenPrice float64
 90		var err error
 91		switch p.apiType {
 92		case APITypeBitFinexV2:
 93			tokenPrice, err = getTokenPriceBitfinex(ctx, client, tokenSymbol)
 94		}
 95		if ctx.Err() != nil {
 96			return
 97		}
 98		if err != nil {
 99			log.Warnw("token price not updated (get error)",
100				"err", err, "token", tokenSymbol, "apiType", p.apiType)
101		}
102		if err = p.db.UpdateTokenValue(tokenSymbol, tokenPrice); err != nil {
103			log.Errorw("token price not updated (db error)",
104				"err", err, "token", tokenSymbol, "apiType", p.apiType)
105		}
106	}
107}
108
109// UpdateTokenList get the registered token symbols from HistoryDB
110func (p *PriceUpdater) UpdateTokenList() error {
111	tokenSymbols, err := p.db.GetTokenSymbols()
112	if err != nil {
113		return tracerr.Wrap(err)
114	}
115	p.tokenSymbols = tokenSymbols
116	return nil
117}