github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/Maverick.go (about) 1 package liquidityscrapers 2 3 import ( 4 "context" 5 "math" 6 "math/big" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/diadata-org/diadata/pkg/dia" 12 "github.com/diadata-org/diadata/pkg/dia/helpers/ethhelper" 13 pairfactorycontract "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/maverick/pairfactory" 14 poolcontract "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/maverick/pool" 15 models "github.com/diadata-org/diadata/pkg/model" 16 "github.com/diadata-org/diadata/pkg/utils" 17 "github.com/ethereum/go-ethereum/accounts/abi/bind" 18 "github.com/ethereum/go-ethereum/common" 19 "github.com/ethereum/go-ethereum/ethclient" 20 ) 21 22 const ( 23 factoryContractAddressDeploymentBlockEth = uint64(17210221) 24 defaultWaitMillis = "25" 25 ) 26 27 type MaverickPool struct { 28 Token0 dia.Asset 29 Token1 dia.Asset 30 ForeignName string 31 Address common.Address 32 } 33 34 type MaverickScraper struct { 35 RestClient *ethclient.Client 36 poolFactoryContractAddress string 37 poolFactoryContractCreationBlock uint64 38 relDB *models.RelDB 39 datastore *models.DB 40 poolChannel chan dia.Pool 41 doneChannel chan bool 42 blockchain string 43 waitTime int 44 exchangeName string 45 pathToPools string 46 } 47 48 func NewMaverickScraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) (s *MaverickScraper) { 49 50 pathToPools := utils.Getenv("PATH_TO_POOLS", "") 51 52 switch exchange.Name { 53 case dia.MaverickExchange: 54 s = makeMaverickPoolScraper(exchange, pathToPools, exchange.RestAPI, relDB, datastore, defaultWaitMillis, factoryContractAddressDeploymentBlockEth) 55 //case dia.MaverickExchangeBNB: 56 // s = makeMaverickScraper(exchange, listenByAddress, fetchPoolsFromDB, restDialEth, wsDialEth, maverickWaitMilliseconds) 57 //case dia.MaverickExchangeZKSync: 58 // s = makeMaverickScraper(exchange, listenByAddress, fetchPoolsFromDB, restDialEth, wsDialEth, maverickWaitMilliseconds) 59 } 60 61 exchangeFactoryContractAddress = exchange.Contract 62 63 go func() { 64 s.fetchPools() 65 }() 66 return s 67 68 } 69 70 func makeMaverickPoolScraper(exchange dia.Exchange, pathToPools string, restDial string, relDB *models.RelDB, datastore *models.DB, waitMilliseconds string, factoryContractDeploymentBlock uint64) *MaverickScraper { 71 var ( 72 restClient *ethclient.Client 73 err error 74 poolChannel = make(chan dia.Pool) 75 doneChannel = make(chan bool) 76 s *MaverickScraper 77 waitTime int 78 ) 79 80 log.Infof("Init rest client for %s.", exchange.BlockChain.Name) 81 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial)) 82 if err != nil { 83 log.Fatal("init rest client: ", err) 84 } 85 86 waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds) 87 waitTime, err = strconv.Atoi(waitTimeString) 88 if err != nil { 89 log.Error("could not parse wait time: ", err) 90 waitTime = 500 91 } 92 93 s = &MaverickScraper{ 94 RestClient: restClient, 95 poolFactoryContractAddress: exchange.Contract, 96 poolFactoryContractCreationBlock: factoryContractDeploymentBlock, 97 relDB: relDB, 98 datastore: datastore, 99 poolChannel: poolChannel, 100 doneChannel: doneChannel, 101 blockchain: exchange.BlockChain.Name, 102 waitTime: waitTime, 103 exchangeName: exchange.Name, 104 pathToPools: pathToPools, 105 } 106 return s 107 } 108 109 func (s *MaverickScraper) fetchPools() { 110 if s.pathToPools != "" { 111 112 // Collect all pool addresses from json file. 113 poolAddresses, err := getAddressesFromConfig("liquidity-scrapers/maverick/" + s.pathToPools) 114 if err != nil { 115 log.Error("fetch pool addresses from config file: ", err) 116 } 117 numPairs := len(poolAddresses) 118 log.Infof("listening to %d pools: %v", numPairs, poolAddresses) 119 120 for _, pool := range poolAddresses { 121 time.Sleep(time.Duration(s.waitTime) * time.Millisecond) 122 pool, err := s.getPoolByAddress(pool) 123 if err != nil { 124 log.Errorln("Error getting pool ", pool) 125 } 126 log.Info("found pool: ", pool) 127 s.poolChannel <- pool 128 } 129 130 } else { 131 132 pools, err := s.getAllPools() 133 if err != nil { 134 log.Fatal(err) 135 } 136 log.Info("Found ", len(pools), " pools") 137 138 for _, pool := range pools { 139 time.Sleep(time.Duration(s.waitTime) * time.Millisecond) 140 log.Info("found pool: ", pool) 141 s.poolChannel <- pool 142 } 143 } 144 s.doneChannel <- true 145 } 146 147 func (s *MaverickScraper) getPoolByAddress(pairAddress common.Address) (pool dia.Pool, err error) { 148 var ( 149 poolContractInstance *poolcontract.PoolCaller 150 token0 dia.Asset 151 token1 dia.Asset 152 ) 153 154 connection := s.RestClient 155 poolContractInstance, err = poolcontract.NewPoolCaller(pairAddress, connection) 156 if err != nil { 157 log.Error(err) 158 return dia.Pool{}, err 159 } 160 161 // Getting tokens from pair 162 address0, _ := poolContractInstance.TokenA(&bind.CallOpts{}) 163 address1, _ := poolContractInstance.TokenB(&bind.CallOpts{}) 164 165 //log.Info(address0) 166 //log.Info(address1) 167 // Only fetch assets from on-chain in case they are not in our DB. 168 token0, err = s.relDB.GetAsset(address0.Hex(), s.blockchain) 169 if err != nil { 170 token0, err = ethhelper.ETHAddressToAsset(address0, s.RestClient, s.blockchain) 171 if err != nil { 172 return 173 } 174 } 175 token1, err = s.relDB.GetAsset(address1.Hex(), s.blockchain) 176 if err != nil { 177 token1, err = ethhelper.ETHAddressToAsset(address1, s.RestClient, s.blockchain) 178 if err != nil { 179 return 180 } 181 } 182 183 // Getting liquidity 184 185 balanceA, err := poolContractInstance.BinBalanceA(&bind.CallOpts{}) 186 balanceB, err := poolContractInstance.BinBalanceB(&bind.CallOpts{}) 187 if err != nil { 188 log.Error("get reserves: ", err) 189 } 190 191 amount0, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(balanceA), new(big.Float).SetFloat64(math.Pow10(18))).Float64() 192 amount1, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(balanceB), new(big.Float).SetFloat64(math.Pow10(18))).Float64() 193 194 // TO DO: Fetch timestamp using block number? 195 pool.Time = time.Now() 196 197 // Fill Pool type with the above data 198 pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{ 199 Asset: token0, 200 Volume: amount0, 201 Index: uint8(0), 202 }) 203 pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{ 204 Asset: token1, 205 Volume: amount1, 206 Index: uint8(1), 207 }) 208 209 // Determine USD liquidity 210 if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) { 211 s.datastore.GetPoolLiquiditiesUSD(&pool, priceCache) 212 } 213 214 pool.Address = pairAddress.Hex() 215 pool.Blockchain = dia.BlockChain{Name: s.blockchain} 216 pool.Exchange = dia.Exchange{Name: s.exchangeName} 217 218 return pool, nil 219 } 220 221 func (s *MaverickScraper) getAllPools() ([]dia.Pool, error) { 222 pools := make([]dia.Pool, 0) 223 224 var factoryContractInstance *pairfactorycontract.PairfactoryFilterer 225 factoryContractInstance, err := pairfactorycontract.NewPairfactoryFilterer(common.HexToAddress(s.poolFactoryContractAddress), s.RestClient) 226 if err != nil { 227 log.Error(err) 228 return pools, err 229 } 230 231 currBlock, err := s.RestClient.BlockNumber(context.Background()) 232 if err != nil { 233 return nil, err 234 } 235 236 var offset uint64 = 2500 237 startBlock := s.poolFactoryContractCreationBlock 238 var endBlock = startBlock + offset 239 240 for { 241 if endBlock > currBlock { 242 endBlock = currBlock 243 } 244 log.Infof("startblock - endblock: %v --- %v ", startBlock, endBlock) 245 246 it, err := factoryContractInstance.FilterPoolCreated( 247 &bind.FilterOpts{ 248 Start: startBlock, 249 End: &endBlock, 250 }) 251 if err != nil { 252 log.Error(err) 253 //return pools, err 254 if endBlock == currBlock { 255 break 256 } 257 258 startBlock = endBlock + 1 259 endBlock = endBlock + offset 260 continue 261 } 262 263 for it.Next() { 264 pool, err := s.getPoolByAddress(it.Event.PoolAddress) 265 if err != nil { 266 log.Warn(err) 267 } else { 268 pools = append(pools, pool) 269 } 270 } 271 if err := it.Close(); err != nil { 272 log.Warn("closing iterator: ", it) 273 } 274 275 if endBlock == currBlock { 276 break 277 } 278 279 startBlock = endBlock + 1 280 endBlock = endBlock + offset 281 } 282 283 return pools, err 284 } 285 286 func (s *MaverickScraper) Pool() chan dia.Pool { 287 return s.poolChannel 288 } 289 290 func (s *MaverickScraper) Done() chan bool { 291 return s.doneChannel 292 }