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

feat: refact osmo to pull individual pools #92

Merged
merged 4 commits into from
Feb 16, 2024
Merged
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
2 changes: 1 addition & 1 deletion config/alliance_default_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package config
var PHOENIX_GRPC = "terra-grpc.polkachu.com:11790"
var MIGALOO_GRPC = "migaloo-grpc.polkachu.com:20790"
var KUJIRA_GRPC = "kujira-grpc.polkachu.com:11890"
var CARBON_GRPC = "carbon-grpc.terra.dev:443"
var CARBON_GRPC = "carbon-grpc.polkachu.com:19690"

var AllianceDefaultConfig = AllianceConfig{
GRPCUrls: []string{MIGALOO_GRPC, KUJIRA_GRPC, CARBON_GRPC},
Expand Down
30 changes: 16 additions & 14 deletions config/default_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var DefaultPriceServerConfig = Config{
Port: 8532,
MetricsPort: 8533,
Sentry: "",
ProviderPriority: []string{"astroport", "binance", "huobi", "coingecko", "kucoin", "bitfinex", "kraken", "okx", "osmosis" /*"bitstamp", */, "bybit" /*"bittrex",*/, "exchangerate", "frankfurter", "fer"},
ProviderPriority: []string{ /*"astroport", "binance", "huobi", "kucoin", "bitfinex", "kraken", "okx", */ "coingecko", "osmosis", "bitstamp" /* "bybit" "bittrex", "exchangerate", "frankfurter", "fer"*/},
Providers: map[string]ProviderConfig{
"astroport": {
Interval: 30,
Expand Down Expand Up @@ -1190,9 +1190,10 @@ var DefaultPriceServerConfig = Config{
},
},
"coingecko": {
Interval: 6,
Interval: 30,
Timeout: 10,
Symbols: []string{
"osmosis",
"bitcoin",
"ethereum",
"binancecoin",
Expand All @@ -1217,20 +1218,21 @@ var DefaultPriceServerConfig = Config{
},
},
"osmosis": {
Interval: 6,
Interval: 30,
Symbols: []string{
"ATOM/USDC",
"AKT/USDC",
"JUNO/USDC",
"SCRT/USDC",
"STARS/USDC",
"ATOM/OSMO",
"AKT/OSMO",
"JUNO/OSMO",
"SCRT/OSMO",
"STARS/OSMO",
"USDC/OSMO",
"INJ/OSMO",
"LUNA/OSMO",
"KAVA/OSMO",
"LINK/OSMO",
"LUNC/OSMO",
"ASH/USDC",
"OSMO/USDC",
"INJ/USDC",
"LUNA/USDC",
"KAVA/USDC",
"LINK/USDC",
"DOT/USDC",
"LUNC/USDC",
},
},
"coinbase": {
Expand Down
1 change: 1 addition & 0 deletions internal/parser/internal/coingecko/coingecko.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var COIN_GECKO_MAPPING = map[string]string{
"white-whale": "WHALE", // White Whale chain
"switcheo": "SWTH", // Carbon chain
"stride-staked-luna": "STLUNA", // Stride chain
"osmosis": "OSMO",
}

func ParseSymbol(symbol string) (string, string, error) {
Expand Down
245 changes: 107 additions & 138 deletions internal/provider/internal/osmosis/osmosis.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import (
"io"
"log"
"net/http"
"strconv"
"strings"
"sync"
"time"

sdktypes "github.com/cosmos/cosmos-sdk/types"
"github.com/terra-money/oracle-feeder-go/config"
"github.com/terra-money/oracle-feeder-go/internal/parser"
internal_types "github.com/terra-money/oracle-feeder-go/internal/types"
"github.com/terra-money/oracle-feeder-go/pkg/types"
"golang.org/x/exp/maps"
)

type OsmosisEndpoint struct {
Expand All @@ -28,28 +27,33 @@ type OsmosisProvider struct {
mu *sync.Mutex
}

var endpoints []OsmosisEndpoint
var endpoints = []OsmosisEndpoint{{
url: "https://osmosis-api.polkachu.com/osmosis/gamm/v1beta1/pools/${POOL_ID}",
used: false,
}, {
url: "https://osmosis-api.polkachu.com/osmosis/gamm/v1beta1/pools/${POOL_ID}",
used: false,
}, {
url: "https://lcd-osmosis.tfl.foundation/osmosis/gamm/v1beta1/pools/${POOL_ID}",
used: false,
}}

var whiteListPoolIds = map[string]string{
"ATOM/USDC": "1",
"AKT/USDC": "3",
// "CRO/USDC": "9", // DOUBLE CHECK
"JUNO/USDC": "497",
// "USTC/USDC": "560", // DOUBLE CHECK
"SCRT/USDC": "584",
"STARS/USDC": "604",
// "DAI/USDC": "674", // DOUBLE CHECK
"OSMO/USDC": "678",
// "EVMOS/USDC": "722", // DOUBLE CHECK
"INJ/USDC": "725",
"LUNA/USDC": "726",
"KAVA/USDC": "730",
"LINK/USDC": "731",
// "MKR/USDC": "733", // DOUBLE CHECK
// "DOT/USDC": "773", // DOUBLE CHECK
"LUNC/USDC": "800",
"ATOM/OSMO": "1",
"AKT/OSMO": "3",
"JUNO/OSMO": "497",
"SCRT/OSMO": "584",
"STARS/OSMO": "604",
"USDC/OSMO": "678",
"INJ/OSMO": "725",
"LUNA/OSMO": "726",
"KAVA/OSMO": "730",
"LINK/OSMO": "731",
"LUNC/OSMO": "800",
"ASH/USDC": "1360",
"OSMO/USDC": "1464",
}
var idToSymbols = make(map[string]string)
var idsBySymbol = make(map[string]string)

func NewOsmosisProvider(config *config.ProviderConfig, stopCh <-chan struct{}) (*OsmosisProvider, error) {
mu := sync.Mutex{}
Expand All @@ -59,16 +63,8 @@ func NewOsmosisProvider(config *config.ProviderConfig, stopCh <-chan struct{}) (
mu: &mu,
}

endpoints = append(endpoints, OsmosisEndpoint{
"https://osmosis-api.polkachu.com/osmosis/gamm/v1beta1/pools?pagination.limit=801",
false,
})
endpoints = append(endpoints, OsmosisEndpoint{
"https://lcd.osmosis.zone/osmosis/gamm/v1beta1/pools?pagination.limit=801",
false,
})
for k, v := range whiteListPoolIds {
idToSymbols[v] = k
idsBySymbol[v] = k
}

go func() {
Expand All @@ -88,23 +84,6 @@ func NewOsmosisProvider(config *config.ProviderConfig, stopCh <-chan struct{}) (
return provider, nil
}

func rotateUrl() (string, error) {
if len(endpoints) == 0 {
return "", fmt.Errorf("No endpoints")
}
for i := range endpoints {
if !endpoints[i].used {
endpoints[i].used = true
return endpoints[i].url, nil
}
}
for i := range endpoints {
endpoints[i].used = false
}
endpoints[0].used = true
return endpoints[0].url, nil
}

func (p *OsmosisProvider) GetPrices() map[string]types.PriceByPair {
result := make(map[string]types.PriceByPair)
p.mu.Lock()
Expand All @@ -122,115 +101,105 @@ func (p *OsmosisProvider) GetPrices() map[string]types.PriceByPair {
}

func (p *OsmosisProvider) fetchAndParse() {
msg, err := fetchPrices(p.config.Symbols)
if err != nil {
log.Printf("%v", err)
} else {
prices, err := parseJSON(msg)
for id, symbol := range idsBySymbol {
res, err := fetchPrice(id)
if err != nil {
log.Printf("%v", err)
} else {
p.mu.Lock()
maps.Copy(p.priceBySymbol, prices)
p.mu.Unlock()
continue
}
var generic internal_types.GenericPoolResponse
if err := json.Unmarshal(res, &generic); err != nil {
fmt.Println("Error:", err)
continue
}
price, err := p.parsePrice(generic, res)
if err != nil {
continue
}
}
}

func fetchPrices(symbols []string) ([]interface{}, error) {
url, err := rotateUrl()
if err != nil {
return nil, err
}
client := &http.Client{Timeout: time.Second * 15}
resp, err := client.Get(url)
if err != nil {
return nil, err
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
jsonObj := make(map[string]interface{})
err = json.Unmarshal(body, &jsonObj)
if err != nil {
log.Printf("parse response error: %v\n", string(body))
return nil, err
}
data, ok := jsonObj["pools"].([]interface{})
if !ok {
log.Printf("no pools: %v\n", string(body))
return nil, err
p.mu.Lock()
p.priceBySymbol[symbol] = internal_types.PriceBySymbol{
Symbol: symbol,
Price: price,
Base: strings.Split(symbol, "/")[0],
Quote: strings.Split(symbol, "/")[1],
Timestamp: uint64(time.Now().Unix()),
}
p.mu.Unlock()
}
return data, nil
}

func parseJSON(msg []interface{}) (map[string]internal_types.PriceBySymbol, error) {
prices := make(map[string]internal_types.PriceBySymbol)
now := time.Now()

osmoPrice := 0.0
osmoPair := "OSMO/USDC"
func (*OsmosisProvider) parsePrice(generic internal_types.GenericPoolResponse, res []byte) (float64, error) {
switch generic.Pool.Type {
case "/osmosis.concentratedliquidity.v1beta1.Pool":
var pool internal_types.OsmosisPoolResponse
if err := json.Unmarshal(res, &pool); err != nil {
return 0, err
}

for _, value := range msg {
item := value.(map[string]interface{})
poolId := item["id"].(string)
symbol, ok := idToSymbols[poolId]
if !ok {
continue
// get the first 18 positons of the price to avoid overflow
price := pool.Pool.CurrentSqrtPrice
if len(price) >= 18 {
price = pool.Pool.CurrentSqrtPrice[:18]
}
base, quote, err := parser.ParseSymbol("osmosis", symbol)
parsedPrice, err := sdktypes.NewDecFromStr(price)
if err != nil {
log.Printf("%v", err)
continue
return 0, err
}
assets, ok := item["pool_assets"].([]interface{})
if !ok || len(assets) != 2 {
log.Printf("invalid pool_assets: %v\n", item)
continue
return parsedPrice.Power(2).Float64()
case "/osmosis.gamm.v1beta1.Pool":
var pool internal_types.OsmosisGammPoolResponse
if err := json.Unmarshal(res, &pool); err != nil {
return 0, err
}
first := assets[0].(map[string]interface{})["token"].(map[string]interface{})
firstAmount, err := strconv.ParseUint(first["amount"].(string), 10, 64)
if err != nil {
continue

// get the first 18 positons of the price to avoid overflow
firstTokenPrice := pool.Pool.PoolAssets[0].Token.Amount
if len(firstTokenPrice) >= 18 {
firstTokenPrice = firstTokenPrice[:18]
}
second := assets[1].(map[string]interface{})["token"].(map[string]interface{})
// secondDenom := second["denom"].(string)
secondAmount, err := strconv.ParseUint(second["amount"].(string), 10, 64)
parsedFirstTokenPrice, err := sdktypes.NewDecFromStr(firstTokenPrice)
if err != nil {
continue
}
price := 0.0
// log.Printf("secondDenom: %s base: %s %v quote: %s %v\n", secondDenom, base, firstAmount, quote, secondAmount)
if firstAmount > 0 && secondAmount > 0 {
// if secondDenom == "uosmo" {
if symbol == osmoPair {
price = float64(firstAmount) / float64(secondAmount)
} else {
price = float64(secondAmount) / float64(firstAmount)
}
return 0, err
}
if symbol == osmoPair {
osmoPrice = price
secondTokenPrice := pool.Pool.PoolAssets[1].Token.Amount
if len(secondTokenPrice) >= 18 {
secondTokenPrice = secondTokenPrice[:18]
}
prices[symbol] = internal_types.PriceBySymbol{
Exchange: "osmosis",
Symbol: symbol,
Base: base,
Quote: quote,
Price: price,
Timestamp: uint64(now.UnixMilli()),
parsedSecondTokenPrice, err := sdktypes.NewDecFromStr(secondTokenPrice)
if err != nil {
return 0, err
}
return parsedSecondTokenPrice.Quo(parsedFirstTokenPrice).Float64()
default:
return 0, fmt.Errorf("unknown pool type: %s", generic.Pool.Type)
}
}

func fetchPrice(poolId string) (res []byte, err error) {
url, err := rotateUrl()
if err != nil {
return nil, err
}
if osmoPrice == 0 {
return nil, fmt.Errorf("no osmo price")
client := &http.Client{Timeout: time.Second * 15}
resp, err := client.Get(strings.Replace(url, "${POOL_ID}", poolId, 1))
if err != nil {
return nil, err
}
for _, pairPrice := range prices {
if pairPrice.Symbol != osmoPair {
pairPrice.Price = pairPrice.Price * osmoPrice
prices[pairPrice.Symbol] = pairPrice

return io.ReadAll(resp.Body)
}

func rotateUrl() (string, error) {
for i := range endpoints {
if !endpoints[i].used {
endpoints[i].used = true
return endpoints[i].url, nil
}
}
return prices, nil
for i := range endpoints {
endpoints[i].used = false
}
endpoints[0].used = true
return endpoints[0].url, nil
}
Loading
Loading