package priceupdater import ( "errors" "fmt" "net/http" "sync" "time" "github.com/dghubble/sling" ) const ( defaultMaxIdleConns = 10 defaultIdleConnTimeout = 10 ) 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: defaultMaxIdleConns, IdleConnTimeout: defaultIdleConnTimeout * 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 { if resp.StatusCode != http.StatusOK { 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 }