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 }