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, error) {
 47	tokenSymbols := []string{}
 48	if !apiType.valid() {
 49		return nil, tracerr.Wrap(fmt.Errorf("Invalid apiType: %v", apiType))
 50	}
 51	return &PriceUpdater{
 52		db:           db,
 53		apiURL:       apiURL,
 54		apiType:      apiType,
 55		tokenSymbols: tokenSymbols,
 56	}, nil
 57}
 58
 59func getTokenPriceBitfinex(ctx context.Context, client *sling.Sling,
 60	tokenSymbol string) (float64, error) {
 61	state := [10]float64{}
 62	req, err := client.New().Get("ticker/t" + tokenSymbol + "USD").Request()
 63	if err != nil {
 64		return 0, tracerr.Wrap(err)
 65	}
 66	res, err := client.Do(req.WithContext(ctx), &state, nil)
 67	if err != nil {
 68		return 0, tracerr.Wrap(err)
 69	}
 70	if res.StatusCode != http.StatusOK {
 71		return 0, fmt.Errorf("http response is not is %v", res.StatusCode)
 72	}
 73	return state[6], nil
 74}
 75
 76// UpdatePrices is triggered by the Coordinator, and internally will update the token prices in the db
 77func (p *PriceUpdater) UpdatePrices(ctx context.Context) {
 78	tr := &http.Transport{
 79		MaxIdleConns:       defaultMaxIdleConns,
 80		IdleConnTimeout:    defaultIdleConnTimeout,
 81		DisableCompression: true,
 82	}
 83	httpClient := &http.Client{Transport: tr}
 84	client := sling.New().Base(p.apiURL).Client(httpClient)
 85
 86	for _, tokenSymbol := range p.tokenSymbols {
 87		var tokenPrice float64
 88		var err error
 89		switch p.apiType {
 90		case APITypeBitFinexV2:
 91			tokenPrice, err = getTokenPriceBitfinex(ctx, client, tokenSymbol)
 92		}
 93		if ctx.Err() != nil {
 94			return
 95		}
 96		if err != nil {
 97			log.Errorw("token price not updated (get error)",
 98				"err", err, "token", tokenSymbol, "apiType", p.apiType)
 99		}
100		if err = p.db.UpdateTokenValue(tokenSymbol, tokenPrice); err != nil {
101			log.Errorw("token price not updated (db error)",
102				"err", err, "token", tokenSymbol, "apiType", p.apiType)
103		}
104	}
105}
106
107// UpdateTokenList get the registered token symbols from HistoryDB
108func (p *PriceUpdater) UpdateTokenList() error {
109	tokenSymbols, err := p.db.GetTokenSymbols()
110	if err != nil {
111		return tracerr.Wrap(err)
112	}
113	p.tokenSymbols = tokenSymbols
114	return nil
115}