arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/priceupdater/priceupdater.go
priceupdater/priceupdater.goRaw
  1package priceupdater
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"net/http"
  7	"sync"
  8	"time"
  9
 10	"github.com/dghubble/sling"
 11)
 12
 13const (
 14	defaultMaxIdleConns    = 10
 15	defaultIdleConnTimeout = 10
 16)
 17
 18var (
 19	// ErrSymbolDoesNotExistInDatabase is used when trying to get a token that is not in the DB
 20	ErrSymbolDoesNotExistInDatabase = errors.New("symbol does not exist in database")
 21)
 22
 23// ConfigPriceUpdater contains the configuration set by the coordinator
 24type ConfigPriceUpdater struct {
 25	RecommendedFee              uint64 // in dollars
 26	RecommendedCreateAccountFee uint64 // in dollars
 27	TokensList                  []string
 28	APIURL                      string
 29}
 30
 31// TokenInfo contains the updated value for the token
 32type TokenInfo struct {
 33	Symbol      string
 34	Value       float64
 35	LastUpdated time.Time
 36}
 37
 38// PriceUpdater definition
 39type PriceUpdater struct {
 40	db     map[string]TokenInfo
 41	config ConfigPriceUpdater
 42	mu     sync.RWMutex
 43}
 44
 45// NewPriceUpdater is the constructor for the updater
 46func NewPriceUpdater(config ConfigPriceUpdater) PriceUpdater {
 47	return PriceUpdater{
 48		db:     make(map[string]TokenInfo),
 49		config: config,
 50	}
 51}
 52
 53// UpdatePrices is triggered by the Coordinator, and internally will update the token prices in the db
 54func (p *PriceUpdater) UpdatePrices() error {
 55	tr := &http.Transport{
 56		MaxIdleConns:       defaultMaxIdleConns,
 57		IdleConnTimeout:    defaultIdleConnTimeout * time.Second,
 58		DisableCompression: true,
 59	}
 60	httpClient := &http.Client{Transport: tr}
 61	client := sling.New().Base(p.config.APIURL).Client(httpClient)
 62
 63	state := [10]float64{}
 64
 65	for _, tokenSymbol := range p.config.TokensList {
 66		resp, err := client.New().Get("ticker/t" + tokenSymbol + "USD").ReceiveSuccess(&state)
 67		if err != nil {
 68			return err
 69		}
 70		// if resp.StatusCode != 200 {
 71		if resp.StatusCode != http.StatusOK {
 72			return fmt.Errorf("Unexpected response status code: %v", resp.StatusCode)
 73		}
 74
 75		tinfo := TokenInfo{
 76			Symbol:      tokenSymbol,
 77			Value:       state[6],
 78			LastUpdated: time.Now(),
 79		}
 80
 81		p.UpdateTokenInfo(tinfo)
 82	}
 83
 84	return nil
 85}
 86
 87// UpdateConfig allows to update the price-updater configuration
 88func (p *PriceUpdater) UpdateConfig(config ConfigPriceUpdater) {
 89	p.mu.Lock()
 90	defer p.mu.Unlock()
 91
 92	p.config = config
 93}
 94
 95// Get one token information
 96func (p *PriceUpdater) Get(tokenSymbol string) (TokenInfo, error) {
 97	var info TokenInfo
 98
 99	// Check if symbol exists in database
100	p.mu.RLock()
101	defer p.mu.RUnlock()
102
103	if info, ok := p.db[tokenSymbol]; ok {
104		return info, nil
105	}
106
107	return info, ErrSymbolDoesNotExistInDatabase
108}
109
110// GetPrices gets all the prices contained in the db
111func (p *PriceUpdater) GetPrices() map[string]TokenInfo {
112	var info = make(map[string]TokenInfo)
113
114	p.mu.RLock()
115	defer p.mu.RUnlock()
116
117	for key, value := range p.db {
118		info[key] = value
119	}
120
121	return info
122}
123
124// UpdateTokenInfo updates one token info
125func (p *PriceUpdater) UpdateTokenInfo(tokenInfo TokenInfo) {
126	p.mu.Lock()
127	defer p.mu.Unlock()
128
129	p.db[tokenInfo.Symbol] = tokenInfo
130}