github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/UniswapV3.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/helpers/ethhelper" 12 uniswapcontractv3 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/uniswapv3" 13 models "github.com/diadata-org/diadata/pkg/model" 14 15 "github.com/diadata-org/diadata/pkg/utils" 16 17 "github.com/diadata-org/diadata/pkg/dia" 18 "github.com/ethereum/go-ethereum/accounts/abi/bind" 19 "github.com/ethereum/go-ethereum/common" 20 "github.com/ethereum/go-ethereum/ethclient" 21 ) 22 23 type UniswapV3Scraper struct { 24 RestClient *ethclient.Client 25 WsClient *ethclient.Client 26 relDB *models.RelDB 27 datastore *models.DB 28 poolChannel chan dia.Pool 29 doneChannel chan bool 30 blockchain string 31 startBlock uint64 32 factoryContract string 33 exchangeName string 34 chunksBlockSize uint64 35 waitTime int 36 } 37 38 // NewUniswapV3Scraper returns a new UniswapV3Scraper. 39 func NewUniswapV3Scraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) *UniswapV3Scraper { 40 log.Info("NewUniswapScraper ", exchange.Name) 41 log.Info("NewUniswapScraper Address ", exchange.Contract) 42 43 var uls *UniswapV3Scraper 44 45 switch exchange.Name { 46 case dia.UniswapExchangeV3: 47 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(12369621)) 48 case dia.UniswapExchangeV3Base: 49 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(1371680)) 50 case dia.UniswapExchangeV3Celo: 51 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(13916355)) 52 case dia.UniswapExchangeV3Polygon: 53 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(22757913)) 54 case dia.UniswapExchangeV3Arbitrum: 55 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(165)) 56 case dia.PanCakeSwapExchangeV3: 57 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(26956207)) 58 case dia.PearlfiExchangeTestnet: 59 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(2890)) 60 case dia.PearlfiExchange: 61 // TO DO: add init block number 62 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(0)) 63 case dia.RamsesV2Exchange: 64 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "200", uint64(90593047)) 65 case dia.NileV2Exchange: 66 uls = makeUniswapV3Scraper(exchange, "", "", relDB, datastore, "2000", uint64(1768866)) 67 } 68 69 go func() { 70 uls.fetchPools() 71 }() 72 return uls 73 } 74 75 // makeUniswapV3Scraper returns a uniswap scraper as used in NewUniswapV3Scraper. 76 func makeUniswapV3Scraper(exchange dia.Exchange, restDial string, wsDial string, relDB *models.RelDB, datastore *models.DB, waitMilliseconds string, startBlock uint64) *UniswapV3Scraper { 77 var ( 78 restClient *ethclient.Client 79 wsClient *ethclient.Client 80 err error 81 poolChannel = make(chan dia.Pool) 82 doneChannel = make(chan bool) 83 uls *UniswapV3Scraper 84 ) 85 86 log.Infof("Init rest and ws client for %s.", exchange.BlockChain.Name) 87 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial)) 88 if err != nil { 89 log.Fatal("init rest client: ", err) 90 } 91 wsClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_WS", wsDial)) 92 if err != nil { 93 log.Fatal("init ws client: ", err) 94 } 95 96 var waitTime int 97 waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds) 98 waitTime, err = strconv.Atoi(waitTimeString) 99 if err != nil { 100 log.Error("could not parse wait time: ", err) 101 waitTime = 500 102 } 103 104 uls = &UniswapV3Scraper{ 105 WsClient: wsClient, 106 RestClient: restClient, 107 relDB: relDB, 108 datastore: datastore, 109 poolChannel: poolChannel, 110 doneChannel: doneChannel, 111 blockchain: exchange.BlockChain.Name, 112 startBlock: startBlock, 113 factoryContract: exchange.Contract, 114 exchangeName: exchange.Name, 115 waitTime: waitTime, 116 } 117 blockSize := utils.Getenv("CHUNKS_BLOCK_SIZE", "10000") 118 uls.chunksBlockSize, err = strconv.ParseUint(blockSize, 10, 64) 119 if err != nil { 120 log.Error("Parse CHUNKS_BLOCK_SIZE: ", err) 121 uls.chunksBlockSize = uint64(10000) 122 } 123 124 return uls 125 } 126 127 // fetchPools fetches all registered pools from on-chain and sends them into the pool channel. 128 func (uls *UniswapV3Scraper) fetchPools() { 129 130 // filter from contract created https://etherscan.io/tx/0x1e20cd6d47d7021ae7e437792823517eeadd835df09dde17ab45afd7a5df4603 131 132 log.Info("get pool creations from address: ", uls.factoryContract) 133 poolsCount := 0 134 contract, err := uniswapcontractv3.NewUniswapV3Filterer(common.HexToAddress(uls.factoryContract), uls.WsClient) 135 if err != nil { 136 log.Error(err) 137 } 138 139 // Iterate over chunks of blocks. 140 currentBlockNumber, err := uls.RestClient.BlockNumber(context.Background()) 141 if err != nil { 142 log.Fatal("Get current block number: ", err) 143 } 144 var startblock, endblock uint64 145 startblock = uls.startBlock 146 endblock = uls.startBlock + uls.chunksBlockSize 147 148 for endblock < currentBlockNumber+uls.chunksBlockSize { 149 150 time.Sleep(time.Duration(uls.waitTime) * time.Millisecond) 151 152 poolCreated, err := contract.FilterPoolCreated( 153 &bind.FilterOpts{ 154 Start: startblock, 155 End: &endblock, 156 }, 157 []common.Address{}, 158 []common.Address{}, 159 []*big.Int{}, 160 ) 161 if err != nil { 162 log.Error("filter pool created: ", err) 163 startblock = endblock 164 endblock = startblock + uls.chunksBlockSize 165 continue 166 } 167 168 for poolCreated.Next() { 169 poolsCount++ 170 var ( 171 pool dia.Pool 172 asset0 dia.Asset 173 asset1 dia.Asset 174 ) 175 log.Info("pools count: ", poolsCount) 176 177 asset0, err = uls.relDB.GetAsset(poolCreated.Event.Token0.Hex(), uls.blockchain) 178 if err != nil { 179 asset0, err = ethhelper.ETHAddressToAsset(poolCreated.Event.Token0, uls.RestClient, uls.blockchain) 180 if err != nil { 181 log.Warn("cannot fetch asset from address ", poolCreated.Event.Token0.Hex()) 182 continue 183 } 184 } 185 asset1, err = uls.relDB.GetAsset(poolCreated.Event.Token1.Hex(), uls.blockchain) 186 if err != nil { 187 asset1, err = ethhelper.ETHAddressToAsset(poolCreated.Event.Token1, uls.RestClient, uls.blockchain) 188 if err != nil { 189 log.Warn("cannot fetch asset from address ", poolCreated.Event.Token1.Hex()) 190 continue 191 } 192 } 193 194 pool.Exchange = dia.Exchange{Name: uls.exchangeName} 195 pool.Blockchain = dia.BlockChain{Name: uls.blockchain} 196 pool.Address = poolCreated.Event.Pool.Hex() 197 198 balance0Big, err := ethhelper.GetBalanceOf(common.HexToAddress(asset0.Address), common.HexToAddress(pool.Address), uls.RestClient) 199 if err != nil { 200 log.Error("GetBalanceOf: ", err) 201 } 202 balance1Big, err := ethhelper.GetBalanceOf(common.HexToAddress(asset1.Address), common.HexToAddress(pool.Address), uls.RestClient) 203 if err != nil { 204 log.Error("GetBalanceOf: ", err) 205 } 206 balance0, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(balance0Big), new(big.Float).SetFloat64(math.Pow10(int(asset0.Decimals)))).Float64() 207 balance1, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(balance1Big), new(big.Float).SetFloat64(math.Pow10(int(asset1.Decimals)))).Float64() 208 209 pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{Asset: asset0, Volume: balance0, Index: uint8(0)}) 210 pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{Asset: asset1, Volume: balance1, Index: uint8(1)}) 211 212 // Determine USD liquidity 213 if balance0 > GLOBAL_NATIVE_LIQUIDITY_THRESHOLD && balance1 > GLOBAL_NATIVE_LIQUIDITY_THRESHOLD { 214 uls.datastore.GetPoolLiquiditiesUSD(&pool, priceCache) 215 } 216 217 pool.Time = time.Now() 218 219 uls.poolChannel <- pool 220 221 } 222 startblock = endblock 223 endblock = startblock + uls.chunksBlockSize 224 225 } 226 uls.doneChannel <- true 227 } 228 229 func (uas *UniswapV3Scraper) Pool() chan dia.Pool { 230 return uas.poolChannel 231 } 232 233 func (uas *UniswapV3Scraper) Done() chan bool { 234 return uas.doneChannel 235 }