github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/VelarScraper.go (about) 1 package liquidityscrapers 2 3 import ( 4 "context" 5 "encoding/hex" 6 "errors" 7 "math/big" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/diadata-org/diadata/pkg/dia" 13 "github.com/diadata-org/diadata/pkg/dia/helpers/stackshelper" 14 "github.com/diadata-org/diadata/pkg/dia/helpers/velarhelper" 15 models "github.com/diadata-org/diadata/pkg/model" 16 "github.com/diadata-org/diadata/pkg/utils" 17 "github.com/sirupsen/logrus" 18 ) 19 20 type VelarLiquidityScraper struct { 21 logger *logrus.Entry 22 api *stackshelper.StacksClient 23 velarClient *velarhelper.VelarClient 24 poolChannel chan dia.Pool 25 doneChannel chan bool 26 blockchain string 27 exchangeName string 28 sleepTimeMilliseconds int 29 relDB *models.RelDB 30 datastore *models.DB 31 handlerType string 32 } 33 34 // NewVelarLiquidityScraper returns a new VelarLiquidityScraper initialized with default values. 35 // The instance is asynchronously scraping as soon as it is created. 36 // ENV values: 37 // 38 // VELAR_SLEEP_TIMEOUT - (optional, millisecond), make timeout between API calls, default "stackshelper.DefaultSleepBetweenCalls" value 39 // VELAR_HIRO_API_KEY - (optional, string), Hiro Stacks API key, improves scraping performance, default = "" 40 // VELAR_DEBUG - (optional, bool), make stdout output with velar client http call, default = false 41 func NewVelarLiquidityScraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) *VelarLiquidityScraper { 42 envPrefix := strings.ToUpper(exchange.Name) 43 44 sleepBetweenCalls := utils.GetenvInt(envPrefix+"_SLEEP_TIMEOUT", stackshelper.DefaultSleepBetweenCalls) 45 hiroAPIKey := utils.Getenv(envPrefix+"_HIRO_API_KEY", "") 46 isDebug := utils.GetenvBool(envPrefix+"_DEBUG", false) 47 48 stacksClient := stackshelper.NewStacksClient( 49 log.WithContext(context.Background()).WithField("context", "StacksClient"), 50 utils.GetTimeDurationFromIntAsMilliseconds(sleepBetweenCalls), 51 hiroAPIKey, 52 isDebug, 53 ) 54 55 velarClient := velarhelper.NewVelarClient( 56 log.WithContext(context.Background()).WithField("context", "VelarClient"), 57 isDebug, 58 ) 59 60 sleepTime, err := strconv.Atoi(utils.Getenv("SLEEP_TIME_MILLISECONDS", "1000")) 61 if err != nil { 62 log.Error("parse SLEEP_TIME_MILLISECONDS: ", err) 63 } 64 s := &VelarLiquidityScraper{ 65 poolChannel: make(chan dia.Pool), 66 doneChannel: make(chan bool), 67 exchangeName: exchange.Name, 68 blockchain: exchange.BlockChain.Name, 69 sleepTimeMilliseconds: sleepTime, 70 api: stacksClient, 71 velarClient: velarClient, 72 relDB: relDB, 73 datastore: datastore, 74 handlerType: "liquidity", 75 } 76 77 s.logger = logrus. 78 New(). 79 WithContext(context.Background()). 80 WithField("handlerType", s.handlerType). 81 WithField("context", "VelarLiquidityScraper") 82 83 go s.fetchPools() 84 85 return s 86 } 87 88 func (s *VelarLiquidityScraper) fetchPools() { 89 tickers, err := s.velarClient.GetAllTickers() 90 if err != nil { 91 s.logger.WithError(err).Error("failed to fetch velar tickers") 92 return 93 } 94 95 for _, t := range tickers { 96 time.Sleep(time.Duration(s.sleepTimeMilliseconds) * time.Millisecond) 97 98 balances, err := s.fetchPoolBalances(t.PoolID) 99 if err != nil { 100 s.logger.WithError(err).Error("failed to fetch velar pool balances") 101 continue 102 } 103 104 tokens := [...]string{t.BaseCurrency, t.TargetCurrency} 105 dbAssets := make([]dia.Asset, 0, len(tokens)) 106 107 for _, address := range tokens { 108 assset, err := s.relDB.GetAsset(address, s.blockchain) 109 if err != nil { 110 s.logger.WithError(err).Errorf("failed to GetAsset with key: %s", address) 111 continue 112 } 113 dbAssets = append(dbAssets, assset) 114 } 115 116 if len(dbAssets) != len(tokens) { 117 s.logger.Error("found less than 2 assets for the pool pair") 118 continue 119 } 120 121 assetVolumes := make([]dia.AssetVolume, len(balances)) 122 123 for i, b := range balances { 124 asset := dbAssets[i] 125 volume, _ := utils.StringToFloat64(b.String(), int64(asset.Decimals)) 126 127 assetVolumes[i] = dia.AssetVolume{ 128 Index: uint8(i), 129 Asset: asset, 130 Volume: volume, 131 } 132 } 133 134 pool := dia.Pool{ 135 Exchange: dia.Exchange{Name: s.exchangeName}, 136 Blockchain: dia.BlockChain{Name: s.blockchain}, 137 Address: t.ID, 138 Time: time.Now(), 139 Assetvolumes: assetVolumes, 140 } 141 142 if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) { 143 s.datastore.GetPoolLiquiditiesUSD(&pool, priceCache) 144 } 145 146 s.logger.WithField("pool", pool).Info("sending pool to poolChannel") 147 s.poolChannel <- pool 148 } 149 150 s.doneChannel <- true 151 } 152 153 func (s *VelarLiquidityScraper) fetchPoolBalances(poolID string) ([]*big.Int, error) { 154 var data []byte 155 var err error 156 157 if strings.HasPrefix(poolID, "21000") { 158 // Handle univ2 liquidity pools 159 args := stackshelper.ContractCallArgs{Sender: velarhelper.DeployerAddressV2} 160 poolContract := "univ2-pool-v1_0_0-" + poolID[4:] 161 162 var resp []byte 163 resp, err = s.api.CallContractFunction(velarhelper.DeployerAddressV2, poolContract, "get-pool", args) 164 if err != nil { 165 return nil, err 166 } 167 168 if entry, ok := stackshelper.DeserializeCVResponse(resp); ok { 169 data = entry 170 } else { 171 err = errors.New("failed to call get-pool: runtime error") 172 } 173 } else { 174 value := new(big.Int) 175 value.SetString(poolID, 10) 176 key := hex.EncodeToString(stackshelper.SerializeCVUint(value)) 177 178 data, err = s.api.GetDataMapEntry(velarhelper.VelarCoreAddress, "pools", key) 179 } 180 181 if err != nil { 182 s.logger.WithError(err).Error("failed to fetch velar pool information") 183 return nil, err 184 } 185 186 pool, err := stackshelper.DeserializeCVTuple(data) 187 if err != nil { 188 s.logger.WithError(err).Error("failed to deserialize cv tuple") 189 return nil, err 190 } 191 192 balance0, err := stackshelper.DeserializeCVUint(pool["reserve0"]) 193 if err != nil { 194 return nil, err 195 } 196 197 balance1, err := stackshelper.DeserializeCVUint(pool["reserve1"]) 198 if err != nil { 199 return nil, err 200 } 201 202 return []*big.Int{balance0, balance1}, nil 203 } 204 205 func (s *VelarLiquidityScraper) Pool() chan dia.Pool { 206 return s.poolChannel 207 } 208 209 func (s *VelarLiquidityScraper) Done() chan bool { 210 return s.doneChannel 211 }