Skip to content

Commit

Permalink
chore: sync up with Umee price feeder (#38)
Browse files Browse the repository at this point in the history
* sync price feeder with umee

* Fix osmosisv2 and mexc

* username in example config
  • Loading branch information
rbajollari authored Feb 9, 2023
1 parent 700ab3a commit ac85b3f
Show file tree
Hide file tree
Showing 29 changed files with 621 additions and 474 deletions.
10 changes: 2 additions & 8 deletions cmd/price-feeder.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"github.com/ojo-network/price-feeder/config"
"github.com/ojo-network/price-feeder/oracle"
"github.com/ojo-network/price-feeder/oracle/client"
"github.com/ojo-network/price-feeder/oracle/provider"
v1 "github.com/ojo-network/price-feeder/router/v1"
)

Expand Down Expand Up @@ -162,18 +161,13 @@ func priceFeederCmdHandler(cmd *cobra.Command, args []string) error {
deviations[deviation.Base] = threshold
}

endpoints := make(map[provider.Name]provider.Endpoint, len(cfg.ProviderEndpoints))
for _, endpoint := range cfg.ProviderEndpoints {
endpoints[endpoint.Name] = endpoint
}

oracle := oracle.New(
logger,
oracleClient,
cfg.CurrencyPairs,
cfg.ProviderPairs(),
providerTimeout,
deviations,
endpoints,
cfg.ProviderEndpointsMap(),
)

telemetryCfg := telemetry.Config{}
Expand Down
35 changes: 25 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/go-playground/validator/v10"
"github.com/ojo-network/price-feeder/oracle/provider"
"github.com/ojo-network/price-feeder/oracle/types"

"github.com/rs/zerolog"
"github.com/spf13/viper"
Expand Down Expand Up @@ -137,6 +138,30 @@ func (c Config) Validate() error {
return validate.Struct(c)
}

func (c Config) ProviderPairs() map[provider.Name][]types.CurrencyPair {
providerPairs := make(map[provider.Name][]types.CurrencyPair)

for _, pair := range c.CurrencyPairs {
for _, provider := range pair.Providers {
providerPairs[provider] = append(providerPairs[provider], types.CurrencyPair{
Base: pair.Base,
Quote: pair.Quote,
})
}
}
return providerPairs
}

// ProviderEndpointsMap converts the provider_endpoints from the config
// file into a map of provider.Endpoint where the key is the provider name.
func (c Config) ProviderEndpointsMap() map[provider.Name]provider.Endpoint {
endpoints := make(map[provider.Name]provider.Endpoint, len(c.ProviderEndpoints))
for _, endpoint := range c.ProviderEndpoints {
endpoints[endpoint.Name] = endpoint
}
return endpoints
}

// ParseConfig attempts to read and parse configuration from the given file path.
// An error is returned if reading or parsing the config fails.
func ParseConfig(configPath string) (Config, error) {
Expand Down Expand Up @@ -206,16 +231,6 @@ func ParseConfig(configPath string) (Config, error) {
}
}

gatePairs := []string{}
for base, providers := range pairs {
if _, ok := providers[provider.ProviderGate]; ok {
gatePairs = append(gatePairs, base)
}
}
if len(gatePairs) > 1 {
return cfg, fmt.Errorf("gate provider does not support multiple pairs: %v", gatePairs)
}

for _, deviation := range cfg.Deviations {
threshold, err := sdk.NewDecFromStr(deviation.Threshold)
if err != nil {
Expand Down
105 changes: 68 additions & 37 deletions config/currency_provider_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
coinGeckoTickersEndpoint = "tickers"
osmosisV2RestURL = "https://api.osmo-api.network.umee.cc"
osmosisV2AssetPairsEndpoint = "assetpairs"
requestTimeout = time.Second * 2
trackingPeriod = time.Hour * 24
)

Expand Down Expand Up @@ -80,7 +81,12 @@ func NewCurrencyProviderTracker(
return nil, err
}

if err := currencyProviderTracker.setCurrencyProviders(); err != nil {
osmosisAPIPairs, err := currencyProviderTracker.getOsmosisAPIPairs()
if err != nil {
return nil, err
}

if err := currencyProviderTracker.setCurrencyProviders(osmosisAPIPairs); err != nil {
return nil, err
}

Expand Down Expand Up @@ -111,51 +117,71 @@ func (t *CurrencyProviderTracker) setCoinIDSymbolMap() error {
}

for _, coin := range listResponse {
t.coinIDSymbolMap[coin.Symbol] = coin.ID
if _, ok := t.coinIDSymbolMap[coin.Symbol]; !ok {
t.coinIDSymbolMap[coin.Symbol] = coin.ID
}
}

return nil
}

// setCurrencyProviders queries CoinGecko's tickers endpoint and the osmosis-api assetpairs
// endpoint to get all the exchanges that support each price feeder currency pair and store
// it in the CurrencyProviders map.
func (t *CurrencyProviderTracker) setCurrencyProviders() error {
for _, pair := range t.pairs {
// check if CoinGecko API supports pair
pairBaseID := t.coinIDSymbolMap[strings.ToLower(pair.Base)]
coinGeckoResp, err := http.Get(fmt.Sprintf("%s/%s/%s", coinGeckoRestURL, pairBaseID, coinGeckoTickersEndpoint))
if err != nil {
return err
}
defer coinGeckoResp.Body.Close()
// getOsmosisAPIPairs queries the osmosis-api assetpairs endpoint to get the asset pairs
// supported by it.
func (t *CurrencyProviderTracker) getOsmosisAPIPairs() (map[string]string, error) {
client := &http.Client{
Timeout: requestTimeout,
}
osmosisAPIPairs := make(map[string]string)

var tickerResponse coinTickerResponse
if err = json.NewDecoder(coinGeckoResp.Body).Decode(&tickerResponse); err != nil {
return err
}
osmosisResp, err := client.Get(fmt.Sprintf("%s/%s", osmosisV2RestURL, osmosisV2AssetPairsEndpoint))
if err != nil {
return nil, err
}
defer osmosisResp.Body.Close()
var assetPairsResponse []assetPair
if err = json.NewDecoder(osmosisResp.Body).Decode(&assetPairsResponse); err != nil {
return nil, err
}

for _, ticker := range tickerResponse.Tickers {
if ticker.Target == pair.Quote {
t.CurrencyProviders[pair.Base] = append(t.CurrencyProviders[pair.Base], ticker.Market.Name)
}
for _, assetPair := range assetPairsResponse {
osmosisAPIPairs[assetPair.Base] = assetPair.Quote
}

return osmosisAPIPairs, nil
}

// setCurrencyProviders queries CoinGecko's tickers endpoint to get all the exchanges
// that support each price feeder currency pair and store it in the CurrencyProviders map.
func (t *CurrencyProviderTracker) setCurrencyProviders(osmosisAPIPairs map[string]string) error {
client := &http.Client{
Timeout: requestTimeout,
}
for _, pair := range t.pairs {
// check if its a pair supported by the osmosis api
if osmosisAPIPairs[strings.ToUpper(pair.Base)] == strings.ToUpper(pair.Quote) {
t.CurrencyProviders[pair.Base] = append(t.CurrencyProviders[pair.Base], "osmosisv2")
}

// check if osmosis-api supports pair
osmosisResp, err := http.Get(fmt.Sprintf("%s/%s", osmosisV2RestURL, osmosisV2AssetPairsEndpoint))
// check if CoinGecko API supports pair
pairBaseID := t.coinIDSymbolMap[strings.ToLower(pair.Base)]
coinGeckoResp, err := client.Get(fmt.Sprintf("%s/%s/%s", coinGeckoRestURL, pairBaseID, coinGeckoTickersEndpoint))
if err != nil {
return err
}
defer osmosisResp.Body.Close()
t.logger.Error().Err(err).Msg(fmt.Sprintf("Failed to query coin gecko api tickers endpoint for %s", pair.Base))
} else {
defer coinGeckoResp.Body.Close()
var tickerResponse coinTickerResponse
if err = json.NewDecoder(coinGeckoResp.Body).Decode(&tickerResponse); err != nil {
return err
}

var assetPairsResponse []assetPair
if err = json.NewDecoder(osmosisResp.Body).Decode(&assetPairsResponse); err != nil {
return err
}
for _, ticker := range tickerResponse.Tickers {
if ticker.Target == pair.Quote {
t.CurrencyProviders[pair.Base] = append(t.CurrencyProviders[pair.Base], ticker.Market.Name)
}
}

for _, assetPair := range assetPairsResponse {
if pair.Base == assetPair.Base && pair.Quote == assetPair.Quote {
t.CurrencyProviders[pair.Base] = append(t.CurrencyProviders[pair.Base], "osmosisv2")
if len(t.CurrencyProviders[pair.Base]) == 0 {
t.logger.Warn().Msg(fmt.Sprintf("Coin gecko found 0 providers for %s", pair.Base))
}
}
}
Expand All @@ -165,10 +191,11 @@ func (t *CurrencyProviderTracker) setCurrencyProviders() error {

// setCurrencyProviderMin will set the minimum amount of providers for each currency
// to the amount of exchanges that support them if it's less than 3. Otherwise it is
// set to 3 providers.
// set to 3 providers. If CoinGecko returns 0 providers for a given pair, the
// minimum providers enforced will default to 3.
func (t *CurrencyProviderTracker) setCurrencyProviderMin() {
for base, exchanges := range t.CurrencyProviders {
if len(exchanges) < 3 {
if len(exchanges) < 3 && len(exchanges) > 0 {
t.CurrencyProviderMin[base] = len(exchanges)
} else {
t.CurrencyProviderMin[base] = 3
Expand All @@ -186,7 +213,11 @@ func (t *CurrencyProviderTracker) trackCurrencyProviders(ctx context.Context) {
case <-ctx.Done():
return
case <-time.After(trackingPeriod):
if err := t.setCurrencyProviders(); err != nil {
osmosisAPIPairs, err := t.getOsmosisAPIPairs()
if err != nil {
t.logger.Error().Err(err).Msg("failed to query osmosis-api for available asset pairs")
}
if err := t.setCurrencyProviders(osmosisAPIPairs); err != nil {
t.logger.Error().Err(err).Msg("failed to set available providers for currencies")
}

Expand Down
1 change: 1 addition & 0 deletions config/supported_assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var (
"BTC": {},
"ETH": {},
"ATOM": {},
"OSMO": {},
}

// SupportedForexCurrencies defines a lookup table for all the supported
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/ojo-network/price-feeder
go 1.19

require (
cosmossdk.io/errors v1.0.0-beta.7
github.com/armon/go-metrics v0.4.1
github.com/cosmos/cosmos-sdk v0.46.7
github.com/go-playground/validator/v10 v10.11.2
Expand Down Expand Up @@ -31,7 +32,6 @@ require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v0.8.0 // indirect
cloud.google.com/go/storage v1.27.0 // indirect
cosmossdk.io/errors v1.0.0-beta.7 // indirect
cosmossdk.io/math v1.0.0-beta.4 // indirect
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
Expand Down
26 changes: 14 additions & 12 deletions oracle/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func ConvertCandlesToUSD(
}

conversionRates := make(map[string]sdk.Dec)
requiredConversions := make(map[provider.Name]types.CurrencyPair)
requiredConversions := make(map[provider.Name][]types.CurrencyPair)

for pairProviderName, pairs := range providerPairs {
for _, pair := range pairs {
Expand Down Expand Up @@ -103,23 +103,25 @@ func ConvertCandlesToUSD(
}

conversionRates[pair.Quote] = cvRate
requiredConversions[pairProviderName] = pair
requiredConversions[pairProviderName] = append(requiredConversions[pairProviderName], pair)
}
}
}

// Convert assets to USD.
for provider, assetMap := range candles {
conversionRate, ok := conversionRates[requiredConversions[provider].Quote]
if !ok {
continue
}
for asset, assetCandles := range assetMap {
if requiredConversions[provider].Base == asset {
for i := range assetCandles {
assetCandles[i].Price = assetCandles[i].Price.Mul(
conversionRate,
)
for _, requiredConversion := range requiredConversions[provider] {
conversionRate, ok := conversionRates[requiredConversion.Quote]
if !ok {
continue
}
for asset, assetCandles := range assetMap {
if requiredConversion.Base == asset {
for i := range assetCandles {
assetCandles[i].Price = assetCandles[i].Price.Mul(
conversionRate,
)
}
}
}
}
Expand Down
14 changes: 1 addition & 13 deletions oracle/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (

"github.com/cosmos/cosmos-sdk/telemetry"
oracletypes "github.com/ojo-network/ojo/x/oracle/types"
"github.com/ojo-network/price-feeder/config"
"github.com/ojo-network/price-feeder/oracle/client"
"github.com/ojo-network/price-feeder/oracle/provider"
"github.com/ojo-network/price-feeder/oracle/types"
Expand Down Expand Up @@ -78,22 +77,11 @@ type Oracle struct {
func New(
logger zerolog.Logger,
oc client.OracleClient,
currencyPairs []config.CurrencyPair,
providerPairs map[provider.Name][]types.CurrencyPair,
providerTimeout time.Duration,
deviations map[string]sdk.Dec,
endpoints map[provider.Name]provider.Endpoint,
) *Oracle {
providerPairs := make(map[provider.Name][]types.CurrencyPair)

for _, pair := range currencyPairs {
for _, provider := range pair.Providers {
providerPairs[provider] = append(providerPairs[provider], types.CurrencyPair{
Base: pair.Base,
Quote: pair.Quote,
})
}
}

return &Oracle{
logger: logger.With().Str("module", "oracle").Logger(),
closer: pfsync.NewCloser(),
Expand Down
Loading

0 comments on commit ac85b3f

Please sign in to comment.