github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/OsmosisScraper.go (about)

     1  package liquidityscrapers
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math"
     7  	"net/url"
     8  	"time"
     9  
    10  	sdk "github.com/cosmos/cosmos-sdk/types"
    11  	"github.com/cosmos/cosmos-sdk/types/query"
    12  	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
    13  	"github.com/diadata-org/diadata/pkg/dia"
    14  	scrapers "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers"
    15  	models "github.com/diadata-org/diadata/pkg/model"
    16  	"github.com/diadata-org/diadata/pkg/utils"
    17  	gammtypes "github.com/osmosis-labs/osmosis/v6/x/gamm/types"
    18  	"google.golang.org/grpc"
    19  	"google.golang.org/grpc/credentials"
    20  	"google.golang.org/grpc/metadata"
    21  )
    22  
    23  type GRPCClient struct {
    24  	ctx      context.Context
    25  	grpcConn *grpc.ClientConn
    26  	gamm     gammtypes.QueryClient
    27  	bank     banktypes.QueryClient
    28  }
    29  
    30  type OsmosisScraper struct {
    31  	blockchain   string
    32  	exchangeName string
    33  	grpcClient   *GRPCClient
    34  	relDb        *models.RelDB
    35  	datastore    *models.DB
    36  	poolChannel  chan dia.Pool
    37  	doneChannel  chan bool
    38  }
    39  
    40  func NewOsmosisScraper(exchange dia.Exchange, relDb *models.RelDB, datastore *models.DB) *OsmosisScraper {
    41  	log.Infof("init rpc client for %s", exchange.BlockChain.Name)
    42  	// encoding := scrapers.NewOsmosisEncoding()
    43  
    44  	cfg := &scrapers.OsmosisConfig{
    45  		Bech32AddrPrefix:  "osmo",
    46  		Bech32PkPrefix:    "osmopub",
    47  		Bech32ValPrefix:   "osmovaloper",
    48  		Bech32PkValPrefix: "osmovalpub",
    49  		Encoding:          nil,
    50  		RpcURL:            utils.Getenv("OSMOSIS_RPC_URL", ""),
    51  	}
    52  
    53  	grpcClient, err := NewGRPCClient(cfg)
    54  	if err != nil {
    55  		panic("failed to create rpc client")
    56  	}
    57  
    58  	scraper := &OsmosisScraper{
    59  		blockchain:   exchange.BlockChain.Name,
    60  		exchangeName: exchange.Name,
    61  		grpcClient:   grpcClient,
    62  		relDb:        relDb,
    63  		datastore:    datastore,
    64  		poolChannel:  make(chan dia.Pool),
    65  		doneChannel:  make(chan bool),
    66  	}
    67  
    68  	go func() {
    69  		err := scraper.loadMarketsMetadata()
    70  		if err != nil {
    71  			log.Error(err)
    72  		}
    73  	}()
    74  
    75  	return scraper
    76  }
    77  
    78  // Load markets and tokens metadata
    79  func (s *OsmosisScraper) loadMarketsMetadata() (err error) {
    80  	log.Infof("loading initial data from pools ...")
    81  	start := time.Now()
    82  	s.loadMarketPools()
    83  	log.Infof("loaded pools data in %.1fs", time.Since(start).Seconds())
    84  	return
    85  }
    86  
    87  // Get Osmosis market pools
    88  func (s *OsmosisScraper) loadMarketPools() {
    89  	s.loadMarketPoolsPage(nil)
    90  }
    91  
    92  func (s *OsmosisScraper) loadMarketPoolsPage(page *[]byte) {
    93  	reqPage := &gammtypes.QueryPoolsRequest{Pagination: &query.PageRequest{}}
    94  	if page != nil {
    95  		reqPage.Pagination.Key = *page
    96  	}
    97  	res, err := s.grpcClient.gamm.Pools(s.grpcClient.ctx, reqPage)
    98  	if err != nil {
    99  		panic(err)
   100  	}
   101  
   102  	go func() {
   103  		if res.Pagination.NextKey != nil {
   104  			s.loadMarketPoolsPage(&res.Pagination.NextKey)
   105  		}
   106  	}()
   107  
   108  	for _, pool := range res.Pools {
   109  		p := &gammtypes.Pool{}
   110  		err = p.Unmarshal(pool.Value)
   111  		if err != nil {
   112  			log.Warn("error parsing pool protobuf msg with type: ", pool.TypeUrl)
   113  			continue
   114  		}
   115  		s.HandlePool(p)
   116  	}
   117  	if res.Pagination.NextKey == nil {
   118  		defer func() {
   119  			s.grpcClient.grpcConn.Close()
   120  			s.Done() <- true
   121  		}()
   122  	}
   123  
   124  }
   125  
   126  func (s *OsmosisScraper) HandlePool(pool *gammtypes.Pool) {
   127  	diaPool := &dia.Pool{}
   128  	for idx, asset := range pool.PoolAssets {
   129  		diaAsset, err := s.relDb.GetAsset(asset.Token.Denom, "Osmosis")
   130  		if err != nil {
   131  			// db didn't have the asset, searching bank sdk
   132  			diaAsset, err = s.GetAssetMetadata(asset)
   133  			if err != nil {
   134  				// we don't have any info on this asset
   135  				log.Warn("Unknown asset: ", asset.Token.Denom)
   136  				return
   137  			}
   138  		}
   139  
   140  		poolAsset := dia.Asset{
   141  			Address:    asset.Token.Denom,
   142  			Blockchain: "Osmosis",
   143  			Decimals:   diaAsset.Decimals,
   144  			Symbol:     diaAsset.Symbol,
   145  			Name:       diaAsset.Name,
   146  		}
   147  		log.Info("pool asset: ", poolAsset)
   148  		volume := asset.Token.Amount.ToDec().MustFloat64() / math.Pow(10, float64(diaAsset.Decimals))
   149  		diaPool.Assetvolumes = append(diaPool.Assetvolumes, dia.AssetVolume{
   150  			Asset:  poolAsset,
   151  			Volume: volume,
   152  			Index:  uint8(idx),
   153  		})
   154  	}
   155  
   156  	// Determine USD liquidity.
   157  	if diaPool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) {
   158  		s.datastore.GetPoolLiquiditiesUSD(diaPool, priceCache)
   159  	}
   160  
   161  	diaPool.Exchange = dia.Exchange{Name: s.exchangeName}
   162  	diaPool.Blockchain = dia.BlockChain{Name: s.blockchain}
   163  	diaPool.Address = pool.Address
   164  
   165  	diaPool.Time = time.Now()
   166  
   167  	s.Pool() <- *diaPool
   168  }
   169  
   170  func (s *OsmosisScraper) GetAssetMetadata(asset gammtypes.PoolAsset) (dia.Asset, error) {
   171  	a := &dia.Asset{}
   172  	res, err := s.grpcClient.bank.DenomMetadata(
   173  		s.grpcClient.ctx,
   174  		&banktypes.QueryDenomMetadataRequest{Denom: asset.Token.Denom},
   175  	)
   176  	if err != nil {
   177  		log.Warn("can't get denom metadata for ", asset.Token.Denom, err)
   178  		return *a, err
   179  	}
   180  	a.Name = res.Metadata.Display
   181  	a.Symbol = res.Metadata.Symbol
   182  	// fallback to Display if Metadata.Symbol is empty
   183  	if a.Symbol == "" {
   184  		a.Symbol = res.Metadata.Display
   185  	}
   186  	denomUnitsMap := make(map[string]uint8)
   187  	for _, du := range res.Metadata.DenomUnits {
   188  		denomUnitsMap[du.Denom] = uint8(du.Exponent)
   189  	}
   190  	a.Decimals = denomUnitsMap[res.Metadata.Display]
   191  	a.Address = res.Metadata.Base
   192  	return *a, nil
   193  }
   194  
   195  func (scraper *OsmosisScraper) Pool() chan dia.Pool {
   196  	return scraper.poolChannel
   197  }
   198  
   199  func (scraper *OsmosisScraper) Done() chan bool {
   200  	return scraper.doneChannel
   201  }
   202  
   203  func NewGRPCClient(conf *scrapers.OsmosisConfig) (*GRPCClient, error) {
   204  	sdk.GetConfig().SetBech32PrefixForAccount(conf.Bech32AddrPrefix, conf.Bech32PkPrefix)
   205  	md := metadata.Pairs()
   206  	ctx := metadata.NewOutgoingContext(context.Background(), md)
   207  	grpcURL, err := url.Parse(conf.RpcURL)
   208  	if err != nil {
   209  		return nil, fmt.Errorf("failed to parse GRPCURL: %s", conf.RpcURL)
   210  	}
   211  	grpcConn, err := grpc.DialContext(
   212  		context.Background(),
   213  		grpcURL.String(),
   214  		grpc.WithTransportCredentials(credentials.NewTLS(nil)),
   215  	)
   216  	if err != nil {
   217  		if grpcConn != nil {
   218  			grpcConn.Close()
   219  		}
   220  		return nil, fmt.Errorf("unable to connect to grpcConn: %s", err)
   221  	}
   222  	gamm := gammtypes.NewQueryClient(grpcConn)
   223  	bank := banktypes.NewQueryClient(grpcConn)
   224  
   225  	c := &GRPCClient{
   226  		ctx:      ctx,
   227  		grpcConn: grpcConn,
   228  		gamm:     gamm,
   229  		bank:     bank,
   230  	}
   231  	return c, nil
   232  }