|
@ -1,8 +1,9 @@ |
|
|
package priceupdater |
|
|
package priceupdater |
|
|
|
|
|
|
|
|
import ( |
|
|
import ( |
|
|
|
|
|
"context" |
|
|
|
|
|
"fmt" |
|
|
"net/http" |
|
|
"net/http" |
|
|
"strconv" |
|
|
|
|
|
"time" |
|
|
"time" |
|
|
|
|
|
|
|
|
"github.com/dghubble/sling" |
|
|
"github.com/dghubble/sling" |
|
@ -13,52 +14,92 @@ import ( |
|
|
|
|
|
|
|
|
const ( |
|
|
const ( |
|
|
defaultMaxIdleConns = 10 |
|
|
defaultMaxIdleConns = 10 |
|
|
defaultIdleConnTimeout = 10 |
|
|
|
|
|
|
|
|
defaultIdleConnTimeout = 2 * time.Second |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// APIType defines the token exchange API
|
|
|
|
|
|
type APIType string |
|
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
|
// APITypeBitFinexV2 is the http API used by bitfinex V2
|
|
|
|
|
|
APITypeBitFinexV2 APIType = "bitfinexV2" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
func (t *APIType) valid() bool { |
|
|
|
|
|
switch *t { |
|
|
|
|
|
case APITypeBitFinexV2: |
|
|
|
|
|
return true |
|
|
|
|
|
default: |
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// PriceUpdater definition
|
|
|
// PriceUpdater definition
|
|
|
type PriceUpdater struct { |
|
|
type PriceUpdater struct { |
|
|
db *historydb.HistoryDB |
|
|
db *historydb.HistoryDB |
|
|
apiURL string |
|
|
apiURL string |
|
|
|
|
|
apiType APIType |
|
|
tokenSymbols []string |
|
|
tokenSymbols []string |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// NewPriceUpdater is the constructor for the updater
|
|
|
// NewPriceUpdater is the constructor for the updater
|
|
|
func NewPriceUpdater(apiURL string, db *historydb.HistoryDB) PriceUpdater { |
|
|
|
|
|
|
|
|
func NewPriceUpdater(apiURL string, apiType APIType, db *historydb.HistoryDB) (*PriceUpdater, error) { |
|
|
tokenSymbols := []string{} |
|
|
tokenSymbols := []string{} |
|
|
return PriceUpdater{ |
|
|
|
|
|
|
|
|
if !apiType.valid() { |
|
|
|
|
|
return nil, tracerr.Wrap(fmt.Errorf("Invalid apiType: %v", apiType)) |
|
|
|
|
|
} |
|
|
|
|
|
return &PriceUpdater{ |
|
|
db: db, |
|
|
db: db, |
|
|
apiURL: apiURL, |
|
|
apiURL: apiURL, |
|
|
|
|
|
apiType: apiType, |
|
|
tokenSymbols: tokenSymbols, |
|
|
tokenSymbols: tokenSymbols, |
|
|
|
|
|
}, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func getTokenPriceBitfinex(ctx context.Context, client *sling.Sling, |
|
|
|
|
|
tokenSymbol string) (float64, error) { |
|
|
|
|
|
state := [10]float64{} |
|
|
|
|
|
req, err := client.New().Get("ticker/t" + tokenSymbol + "USD").Request() |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return 0, tracerr.Wrap(err) |
|
|
|
|
|
} |
|
|
|
|
|
res, err := client.Do(req.WithContext(ctx), &state, nil) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return 0, tracerr.Wrap(err) |
|
|
|
|
|
} |
|
|
|
|
|
if res.StatusCode != http.StatusOK { |
|
|
|
|
|
return 0, fmt.Errorf("http response is not is %v", res.StatusCode) |
|
|
} |
|
|
} |
|
|
|
|
|
return state[6], nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// UpdatePrices is triggered by the Coordinator, and internally will update the token prices in the db
|
|
|
// UpdatePrices is triggered by the Coordinator, and internally will update the token prices in the db
|
|
|
func (p *PriceUpdater) UpdatePrices() { |
|
|
|
|
|
|
|
|
func (p *PriceUpdater) UpdatePrices(ctx context.Context) { |
|
|
tr := &http.Transport{ |
|
|
tr := &http.Transport{ |
|
|
MaxIdleConns: defaultMaxIdleConns, |
|
|
MaxIdleConns: defaultMaxIdleConns, |
|
|
IdleConnTimeout: defaultIdleConnTimeout * time.Second, |
|
|
|
|
|
|
|
|
IdleConnTimeout: defaultIdleConnTimeout, |
|
|
DisableCompression: true, |
|
|
DisableCompression: true, |
|
|
} |
|
|
} |
|
|
httpClient := &http.Client{Transport: tr} |
|
|
httpClient := &http.Client{Transport: tr} |
|
|
client := sling.New().Base(p.apiURL).Client(httpClient) |
|
|
client := sling.New().Base(p.apiURL).Client(httpClient) |
|
|
|
|
|
|
|
|
state := [10]float64{} |
|
|
|
|
|
|
|
|
|
|
|
for _, tokenSymbol := range p.tokenSymbols { |
|
|
for _, tokenSymbol := range p.tokenSymbols { |
|
|
resp, err := client.New().Get("ticker/t" + tokenSymbol + "USD").ReceiveSuccess(&state) |
|
|
|
|
|
errString := tokenSymbol + " not updated, error: " |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
log.Error(errString + err.Error()) |
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
var tokenPrice float64 |
|
|
|
|
|
var err error |
|
|
|
|
|
switch p.apiType { |
|
|
|
|
|
case APITypeBitFinexV2: |
|
|
|
|
|
tokenPrice, err = getTokenPriceBitfinex(ctx, client, tokenSymbol) |
|
|
} |
|
|
} |
|
|
if resp.StatusCode != http.StatusOK { |
|
|
|
|
|
log.Error(errString + "response is not 200, is " + strconv.Itoa(resp.StatusCode)) |
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
if ctx.Err() != nil { |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
err = p.db.UpdateTokenValue(tokenSymbol, state[6]) |
|
|
|
|
|
if err != nil { |
|
|
if err != nil { |
|
|
log.Error(errString + err.Error()) |
|
|
|
|
|
|
|
|
log.Errorw("token price not updated (get error)", |
|
|
|
|
|
"err", err, "token", tokenSymbol, "apiType", p.apiType) |
|
|
|
|
|
} |
|
|
|
|
|
if err = p.db.UpdateTokenValue(tokenSymbol, tokenPrice); err != nil { |
|
|
|
|
|
log.Errorw("token price not updated (db error)", |
|
|
|
|
|
"err", err, "token", tokenSymbol, "apiType", p.apiType) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|