github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/UniswapV4.go (about) 1 package liquidityscrapers 2 3 import ( 4 "context" 5 "encoding/hex" 6 "strconv" 7 "strings" 8 "time" 9 10 "github.com/diadata-org/diadata/pkg/dia/helpers/ethhelper" 11 uniswapcontractv4 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/uniswapv4" 12 models "github.com/diadata-org/diadata/pkg/model" 13 14 "github.com/diadata-org/diadata/pkg/utils" 15 16 "github.com/diadata-org/diadata/pkg/dia" 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 type UniswapV4Scraper struct { 23 RestClient *ethclient.Client 24 WsClient *ethclient.Client 25 relDB *models.RelDB 26 datastore *models.DB 27 poolChannel chan dia.Pool 28 doneChannel chan bool 29 blockchain string 30 startBlock uint64 31 factoryContract string 32 exchangeName string 33 chunksBlockSize uint64 34 waitTime int 35 } 36 37 const univ4_start_block = uint64(21688329) 38 39 // NewUniswapV4Scraper returns a new UniswapV4Scraper. 40 func NewUniswapV4Scraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) *UniswapV4Scraper { 41 log.Info("NewUniswapV4Scraper ", exchange.Name) 42 log.Info("NewUniswapV4Scraper Address ", exchange.Contract) 43 44 var uls *UniswapV4Scraper 45 46 switch exchange.Name { 47 case dia.UniswapExchangeV4: 48 uls = makeUniswapV4Scraper(exchange, "", "", relDB, datastore, "200", univ4_start_block) 49 } 50 51 go func() { 52 uls.fetchPools() 53 }() 54 return uls 55 } 56 57 // makeUniswapV4Scraper returns a uniswap scraper as used in NewUniswapV4Scraper. 58 func makeUniswapV4Scraper(exchange dia.Exchange, restDial string, wsDial string, relDB *models.RelDB, datastore *models.DB, waitMilliseconds string, startBlock uint64) *UniswapV4Scraper { 59 var ( 60 restClient *ethclient.Client 61 wsClient *ethclient.Client 62 err error 63 poolChannel = make(chan dia.Pool) 64 doneChannel = make(chan bool) 65 uls *UniswapV4Scraper 66 ) 67 68 log.Infof("Init rest and ws client for %s.", exchange.BlockChain.Name) 69 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial)) 70 if err != nil { 71 log.Fatal("init rest client: ", err) 72 } 73 wsClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_WS", wsDial)) 74 if err != nil { 75 log.Fatal("init ws client: ", err) 76 } 77 78 var waitTime int 79 waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds) 80 waitTime, err = strconv.Atoi(waitTimeString) 81 if err != nil { 82 log.Error("could not parse wait time: ", err) 83 waitTime = 500 84 } 85 86 uls = &UniswapV4Scraper{ 87 WsClient: wsClient, 88 RestClient: restClient, 89 relDB: relDB, 90 datastore: datastore, 91 poolChannel: poolChannel, 92 doneChannel: doneChannel, 93 blockchain: exchange.BlockChain.Name, 94 startBlock: startBlock, 95 factoryContract: exchange.Contract, 96 exchangeName: exchange.Name, 97 waitTime: waitTime, 98 } 99 blockSize := utils.Getenv("CHUNKS_BLOCK_SIZE", "10000") 100 uls.chunksBlockSize, err = strconv.ParseUint(blockSize, 10, 64) 101 if err != nil { 102 log.Error("Parse CHUNKS_BLOCK_SIZE: ", err) 103 uls.chunksBlockSize = uint64(10000) 104 } 105 106 return uls 107 } 108 109 // fetchPools fetches all registered pools from on-chain and sends them into the pool channel. 110 func (uls *UniswapV4Scraper) fetchPools() { 111 112 // filter from contract created https://etherscan.io/tx/0x1e20cd6d47d7021ae7e437792823517eeadd835df09dde17ab45afd7a5df4603 113 log.Info("get pool creations from address: ", uls.factoryContract) 114 poolsCount := 0 115 // contract, err := uniswapcontractv4.NewUniswapV4Filterer(common.HexToAddress(uls.factoryContract), uls.WsClient) 116 contract, err := uniswapcontractv4.NewPoolmanagerFilterer(common.HexToAddress(uls.factoryContract), uls.WsClient) 117 if err != nil { 118 log.Error(err) 119 } 120 121 // Iterate over chunks of blocks. 122 currentBlockNumber, err := uls.RestClient.BlockNumber(context.Background()) 123 if err != nil { 124 log.Fatal("Get current block number: ", err) 125 } 126 var startblock, endblock uint64 127 startblock = uls.startBlock 128 endblock = uls.startBlock + uls.chunksBlockSize 129 130 for endblock < currentBlockNumber+uls.chunksBlockSize { 131 132 time.Sleep(time.Duration(uls.waitTime) * time.Millisecond) 133 134 poolCreated, err := contract.FilterInitialize( 135 &bind.FilterOpts{ 136 Start: startblock, 137 End: &endblock, 138 }, 139 [][32]byte{}, 140 []common.Address{}, 141 []common.Address{}, 142 ) 143 if err != nil { 144 log.Error("filter pool created: ", err) 145 startblock = endblock 146 endblock = startblock + uls.chunksBlockSize 147 continue 148 } 149 150 for poolCreated.Next() { 151 poolsCount++ 152 var ( 153 pool dia.Pool 154 asset0 dia.Asset 155 asset1 dia.Asset 156 ) 157 log.Info("pools count: ", poolsCount) 158 159 asset0, err = uls.relDB.GetAsset(poolCreated.Event.Currency0.Hex(), uls.blockchain) 160 if err != nil { 161 asset0, err = ethhelper.ETHAddressToAsset(poolCreated.Event.Currency0, uls.RestClient, uls.blockchain) 162 if err != nil { 163 log.Warn("cannot fetch asset from address ", poolCreated.Event.Currency0.Hex()) 164 continue 165 } 166 } 167 asset1, err = uls.relDB.GetAsset(poolCreated.Event.Currency1.Hex(), uls.blockchain) 168 if err != nil { 169 asset1, err = ethhelper.ETHAddressToAsset(poolCreated.Event.Currency1, uls.RestClient, uls.blockchain) 170 if err != nil { 171 log.Warn("cannot fetch asset from address ", poolCreated.Event.Currency1.Hex()) 172 continue 173 } 174 } 175 176 log.Infof("%s-%s : %s -- %s", asset0.Symbol, asset1.Symbol, poolCreated.Event.Currency0.Hex(), poolCreated.Event.Currency1.Hex()) 177 178 pool.Exchange = dia.Exchange{Name: uls.exchangeName} 179 pool.Blockchain = dia.BlockChain{Name: uls.blockchain} 180 pool.Address = hex.EncodeToString(poolCreated.Event.Id[:]) 181 182 pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{Asset: asset0, Volume: float64(0), Index: uint8(0)}) 183 pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{Asset: asset1, Volume: float64(0), Index: uint8(1)}) 184 pool.Time = time.Now() 185 186 uls.poolChannel <- pool 187 188 } 189 startblock = endblock 190 endblock = startblock + uls.chunksBlockSize 191 192 } 193 uls.doneChannel <- true 194 } 195 196 func (uls *UniswapV4Scraper) Pool() chan dia.Pool { 197 return uls.poolChannel 198 } 199 200 func (uls *UniswapV4Scraper) Done() chan bool { 201 return uls.doneChannel 202 }