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}