mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 11:26:44 +01:00
Priceupdater-v0 (#23)
* First price-updater implementation * Lint corrections * Structs revision * Read Write Mutex * Review price updater and apply small changes Co-authored-by: Toni Ramírez <toni@iden3.com> Co-authored-by: Eduard S <eduard@iden3.io>
This commit is contained in:
136
priceupdater/priceupdater.go
Normal file
136
priceupdater/priceupdater.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package priceupdater
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dghubble/sling"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrSymbolDoesNotExistInDatabase is used when trying to get a token that is not in the DB
|
||||||
|
ErrSymbolDoesNotExistInDatabase = errors.New("symbol does not exist in database")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigPriceUpdater contains the configuration set by the coordinator
|
||||||
|
type ConfigPriceUpdater struct {
|
||||||
|
RecommendedFee uint64 // in dollars
|
||||||
|
RecommendedCreateAccountFee uint64 // in dollars
|
||||||
|
TokensList []string
|
||||||
|
APIURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenInfo contains the updated value for the token
|
||||||
|
type TokenInfo struct {
|
||||||
|
Symbol string
|
||||||
|
Value float64
|
||||||
|
LastUpdated time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// PriceUpdater definition
|
||||||
|
type PriceUpdater struct {
|
||||||
|
db map[string]TokenInfo
|
||||||
|
config ConfigPriceUpdater
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPriceUpdater is the constructor for the updater
|
||||||
|
func NewPriceUpdater(config ConfigPriceUpdater) PriceUpdater {
|
||||||
|
|
||||||
|
return PriceUpdater{
|
||||||
|
db: make(map[string]TokenInfo),
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePrices is triggered by the Coordinator, and internally will update the token prices in the db
|
||||||
|
func (p *PriceUpdater) UpdatePrices() error {
|
||||||
|
|
||||||
|
tr := &http.Transport{
|
||||||
|
MaxIdleConns: 10,
|
||||||
|
IdleConnTimeout: 10 * time.Second,
|
||||||
|
DisableCompression: true,
|
||||||
|
}
|
||||||
|
httpClient := &http.Client{Transport: tr}
|
||||||
|
client := sling.New().Base(p.config.APIURL).Client(httpClient)
|
||||||
|
|
||||||
|
state := [10]float64{}
|
||||||
|
|
||||||
|
for _, tokenSymbol := range p.config.TokensList {
|
||||||
|
|
||||||
|
resp, err := client.New().Get("ticker/t" + tokenSymbol + "USD").ReceiveSuccess(&state)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("Unexpected response status code: %v", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
tinfo := TokenInfo{
|
||||||
|
Symbol: tokenSymbol,
|
||||||
|
Value: state[6],
|
||||||
|
LastUpdated: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
p.UpdateTokenInfo(tinfo)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig allows to update the price-updater configuration
|
||||||
|
func (p *PriceUpdater) UpdateConfig(config ConfigPriceUpdater) {
|
||||||
|
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
|
p.config = config
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get one token information
|
||||||
|
func (p *PriceUpdater) Get(tokenSymbol string) (TokenInfo, error) {
|
||||||
|
|
||||||
|
var info TokenInfo
|
||||||
|
|
||||||
|
// Check if symbol exists in database
|
||||||
|
p.mu.RLock()
|
||||||
|
defer p.mu.RUnlock()
|
||||||
|
|
||||||
|
if info, ok := p.db[tokenSymbol]; ok {
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, ErrSymbolDoesNotExistInDatabase
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrices gets all the prices contained in the db
|
||||||
|
func (p *PriceUpdater) GetPrices() map[string]TokenInfo {
|
||||||
|
|
||||||
|
var info = make(map[string]TokenInfo)
|
||||||
|
|
||||||
|
p.mu.RLock()
|
||||||
|
defer p.mu.RUnlock()
|
||||||
|
|
||||||
|
for key, value := range p.db {
|
||||||
|
info[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateTokenInfo updates one token info
|
||||||
|
func (p *PriceUpdater) UpdateTokenInfo(tokenInfo TokenInfo) {
|
||||||
|
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
|
p.db[tokenInfo.Symbol] = tokenInfo
|
||||||
|
|
||||||
|
}
|
||||||
40
priceupdater/priceupdater_test.go
Normal file
40
priceupdater/priceupdater_test.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package priceupdater
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCon(t *testing.T) {
|
||||||
|
|
||||||
|
config := ConfigPriceUpdater{
|
||||||
|
|
||||||
|
RecommendedFee: 1,
|
||||||
|
RecommendedCreateAccountFee: 1,
|
||||||
|
TokensList: []string{"ETH", "NEC"},
|
||||||
|
APIURL: "https://api-pub.bitfinex.com/v2/",
|
||||||
|
}
|
||||||
|
|
||||||
|
pud := NewPriceUpdater(config)
|
||||||
|
|
||||||
|
err := pud.UpdatePrices()
|
||||||
|
assert.Equal(t, err, nil)
|
||||||
|
|
||||||
|
info, _ := pud.Get("ETH")
|
||||||
|
assert.NotZero(t, info.Value)
|
||||||
|
|
||||||
|
info2, _ := pud.Get("NEC")
|
||||||
|
assert.NotZero(t, info2.Value)
|
||||||
|
|
||||||
|
info3, err := pud.Get("INVENTED")
|
||||||
|
if assert.Error(t, err) {
|
||||||
|
assert.Equal(t, ErrSymbolDoesNotExistInDatabase, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, info3.Value, float64(0))
|
||||||
|
|
||||||
|
prices := pud.GetPrices()
|
||||||
|
assert.Equal(t, prices["ETH"], info)
|
||||||
|
assert.Equal(t, prices["NEC"], info2)
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user