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