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}