Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: helix requires uppercase, and doesn't provide volume #75

Open
wants to merge 4 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/price-feeder.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ func priceFeederCmdHandler(cmd *cobra.Command, args []string) error {
cfg.Decimals,
cfg.Periods,
volumeDatabase,
cfg.Helix,
)

telemetryCfg := telemetry.Config{}
Expand Down
5 changes: 5 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ type (
Decimals map[string]map[string]int `toml:"decimals"`
Periods map[string]map[string]int `toml:"periods"`
UrlSets map[string]UrlSet `toml:"url_set"`
Helix Helix `toml:"helix"`
}

// Server defines the API server configuration.
Expand Down Expand Up @@ -230,6 +231,10 @@ type (
UrlSet struct {
Urls []string `toml:"urls"`
}

Helix struct {
TokenFile string `toml:"token_file"`
}
)

// telemetryValidation is custom validation for the Telemetry struct.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ require (
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/go-toolsmith/astcast v1.0.0 // indirect
github.com/go-toolsmith/astcopy v1.0.2 // indirect
github.com/go-toolsmith/astequal v1.0.3 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
Expand Down
15 changes: 10 additions & 5 deletions oracle/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ type Oracle struct {
periods map[string]map[string]int
volumeDatabase *sql.DB

mtx sync.RWMutex
prices map[string]sdk.Dec
healthchecks map[string]http.Client
mtx sync.RWMutex
prices map[string]sdk.Dec
healthchecks map[string]http.Client
helixTokenFile string
}

func New(
Expand All @@ -82,6 +83,7 @@ func New(
decimals map[string]map[string]int,
periods map[string]map[string]int,
volumeDatabase *sql.DB,
helix config.Helix,
) *Oracle {
providerPairs := make(map[provider.Name][]types.CurrencyPair)
for _, pair := range currencyPairs {
Expand Down Expand Up @@ -125,6 +127,7 @@ func New(
decimals: decimals,
periods: periods,
volumeDatabase: volumeDatabase,
helixTokenFile: helix.TokenFile,
}
}

Expand Down Expand Up @@ -178,7 +181,7 @@ func (o *Oracle) GetPrices() sdk.DecCoins {
// SetPrices retrieves all the prices and candles from our set of providers as
// determined in the config. If candles are available, uses TVWAP in order
// to determine prices. If candles are not available, uses the most recent prices
// with VWAP. Warns the the user of any missing prices, and filters out any faulty
// with VWAP. Warns the user of any missing prices, and filters out any faulty
// providers which do not report prices or candles within 2𝜎 of the others.
func (o *Oracle) SetPrices(ctx context.Context) error {
g := new(errgroup.Group)
Expand Down Expand Up @@ -206,6 +209,7 @@ func (o *Oracle) SetPrices(ctx context.Context) error {
providerName,
o.logger,
endpoint,
o.helixTokenFile,
o.providerPairs[providerName]...,
)
if err != nil {
Expand Down Expand Up @@ -383,6 +387,7 @@ func NewProvider(
providerName provider.Name,
logger zerolog.Logger,
endpoint provider.Endpoint,
helixTokenFile string,
providerPairs ...types.CurrencyPair,
) (provider.Provider, error) {
endpoint.Name = providerName
Expand Down Expand Up @@ -431,7 +436,7 @@ func NewProvider(
case provider.ProviderGate:
return provider.NewGateProvider(ctx, providerLogger, endpoint, providerPairs...)
case provider.ProviderHelix:
return provider.NewHelixProvider(ctx, providerLogger, endpoint, providerPairs...)
return provider.NewHelixProvider(ctx, providerLogger, endpoint, helixTokenFile, providerPairs...)
case provider.ProviderHitBtc:
return provider.NewHitBtcProvider(ctx, providerLogger, endpoint, providerPairs...)
case provider.ProviderHuobi:
Expand Down
101 changes: 97 additions & 4 deletions oracle/provider/helix.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package provider
import (
"context"
"encoding/json"
"io"
"net/http"
"strings"
"time"

"price-feeder/oracle/types"
Expand All @@ -26,6 +29,7 @@ type (
HelixProvider struct {
provider
contracts map[string]string
token map[string]HelixToken
}

HelixMarketsResponse struct {
Expand All @@ -34,18 +38,31 @@ type (

HelixMarket struct {
Market struct {
Ticker string `json:"ticker"`
Ticker string `json:"ticker"`
BaseDenom string `json:"base_denom"`
QuoteDenom string `json:"quote_denom"`
} `json:"market"`
MidPriceAndTob struct {
Price string `json:"mid_price"`
Buy string `json:"best_buy_price"`
Sell string `json:"best_sell_price"`
} `json:"mid_price_and_tob"`
}

HelixToken struct {
Address string `json:"address"`
Decimals uint64 `json:"decimals"`
Symbol string `json:"symbol"`
Name string `json:"name"`
Denom string `json:"denom"`
}
)

func NewHelixProvider(
ctx context.Context,
logger zerolog.Logger,
endpoints Endpoint,
helixTokenFile string,
pairs ...types.CurrencyPair,
) (*HelixProvider, error) {
provider := &HelixProvider{}
Expand All @@ -58,6 +75,26 @@ func NewHelixProvider(
nil,
)

res, err := http.Get(helixTokenFile)
if err != nil {
return nil, err
}
body, err := io.ReadAll(res.Body)
if err != nil {
panic(err.Error())
}

var response []HelixToken
err = json.Unmarshal(body, &response)
if err != nil {
return nil, err
}
tokens := map[string]HelixToken{}
for _, helixToken := range response {
tokens[strings.ToUpper(helixToken.Denom)] = helixToken
}
provider.token = tokens

provider.contracts = provider.endpoints.ContractAddresses

availablePairs, _ := provider.GetAvailablePairs()
Expand All @@ -83,10 +120,60 @@ func (p *HelixProvider) Poll() error {
continue
}

pair, found := p.getPair(market.Market.Ticker)
if !found {
continue
}

rawPrice := strToDec(market.MidPriceAndTob.Price)
if rawPrice.IsNil() {
p.logger.Info().Str("ticker", market.Market.Ticker).Msg("No Price available from Helix")
continue
}
buyPrice := strToDec(market.MidPriceAndTob.Buy)
if buyPrice.IsNil() {
p.logger.Info().Str("ticker", market.Market.Ticker).Msg("No Buy Price available from Helix")
continue
}
sellPrice := strToDec(market.MidPriceAndTob.Sell)
if sellPrice.IsNil() {
p.logger.Info().Str("ticker", market.Market.Ticker).Msg("No Sell Price available from Helix")
continue
}

// |((sell-buy)/sell)*100|
ratio := sellPrice.Sub(buyPrice).Quo(sellPrice).Mul(sdk.NewDec(100)).Abs()
if ratio.GT(sdk.NewDec(10)) {
p.logger.Warn().Str("ticker", market.Market.Ticker).Msg("buy/sell spread is larger than 10%. Skipping")
continue
}

baseToken, found := p.token[strings.ToUpper(market.Market.BaseDenom)]
if !found {
p.logger.Warn().Str("token", pair.Base).Str("denom", market.Market.BaseDenom).Msg("token not found in helix Token list")
continue
}
quoteToken, found := p.token[strings.ToUpper(market.Market.QuoteDenom)]
if !found {
p.logger.Warn().Str("token", pair.Quote).Str("denom", market.Market.QuoteDenom).Msg("token not found in helix Token list")
continue
}
var decimalDifference uint64
var multiplier sdk.Dec
if quoteToken.Decimals > baseToken.Decimals {
decimalDifference = quoteToken.Decimals - baseToken.Decimals
multiplier = sdk.NewDec(10).Power(decimalDifference)
rawPrice = rawPrice.Mul(invertDec(multiplier))
} else {
decimalDifference = baseToken.Decimals - quoteToken.Decimals
multiplier = sdk.NewDec(10).Power(decimalDifference)
rawPrice = rawPrice.Mul(multiplier)
}

p.setTickerPrice(
market.Market.Ticker,
strToDec(market.MidPriceAndTob.Price),
sdk.ZeroDec(),
rawPrice,
sdk.OneDec(), // helix doesn't appear to report volume
timestamp,
)
}
Expand All @@ -108,7 +195,13 @@ func (p *HelixProvider) GetMarkets() ([]HelixMarket, error) {
return nil, err
}

return response.Markets, nil
var markets []HelixMarket
for _, market := range response.Markets {
market.Market.Ticker = strings.ToUpper(market.Market.Ticker)
markets = append(markets, market)
}

return markets, nil
}

func (p *HelixProvider) GetAvailablePairs() (map[string]struct{}, error) {
Expand Down