Skip to content

Commit

Permalink
feat: refact osmo to pull individual pools
Browse files Browse the repository at this point in the history
  • Loading branch information
emidev98 committed Feb 15, 2024
1 parent a2c9539 commit cf2d641
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 152 deletions.
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

0 comments on commit cf2d641

Please sign in to comment.