github.com/diadata-org/diadata@v1.4.593/pkg/dia/service/assetservice/source/uniswapv3.go (about) 1 package source 2 3 import ( 4 "context" 5 "math/big" 6 "strconv" 7 "strings" 8 "time" 9 10 uniswapcontract "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/uniswap" 11 uniswapcontractv3 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/uniswapv3" 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 var numBlocksQuery = uint64(1000) 23 24 type UniswapV3AssetSource struct { 25 RestClient *ethclient.Client 26 WsClient *ethclient.Client 27 relDB *models.RelDB 28 // signaling channels for session initialization and finishing 29 assetChannel chan dia.Asset 30 doneChannel chan bool 31 exchange dia.Exchange 32 startBlock uint64 33 factoryContract string 34 waitTime int 35 } 36 37 // NewUniswapV3AssetSource returns a new UniswapV3AssetSource 38 func NewUniswapV3AssetSource(exchange dia.Exchange, relDB *models.RelDB) *UniswapV3AssetSource { 39 log.Info("NewUniswapV3Scraper ", exchange.Name) 40 log.Info("NewUniswapV3Scraper Address ", exchange.Contract) 41 42 var uas *UniswapV3AssetSource 43 44 switch exchange.Name { 45 case dia.UniswapExchangeV3: 46 uas = makeUniswapV3AssetSource(exchange, "", "", relDB, "200", uint64(12369621)) 47 case dia.UniswapExchangeV3Polygon: 48 uas = makeUniswapV3AssetSource(exchange, "", "", relDB, "200", uint64(22757913)) 49 case dia.UniswapExchangeV3Arbitrum: 50 uas = makeUniswapV3AssetSource(exchange, "", "", relDB, "200", uint64(165)) 51 case dia.UniswapExchangeV3Base: 52 uas = makeUniswapV3AssetSource(exchange, "", "", relDB, "200", uint64(1371680)) 53 case dia.UniswapExchangeV3Celo: 54 uas = makeUniswapV3AssetSource(exchange, "", "", relDB, "200", uint64(13916355)) 55 case dia.PanCakeSwapExchangeV3: 56 uas = makeUniswapV3AssetSource(exchange, "", "", relDB, "200", uint64(26956207)) 57 case dia.RamsesV2Exchange: 58 uas = makeUniswapV3AssetSource(exchange, "", "", relDB, "200", uint64(90593047)) 59 case dia.NileV2Exchange: 60 uas = makeUniswapV3AssetSource(exchange, "", "", relDB, "200", uint64(1768866)) 61 } 62 63 go func() { 64 uas.fetchAssets() 65 }() 66 return uas 67 68 } 69 70 // makeUniswapV3AssetSource returns a uniswap asset source. 71 func makeUniswapV3AssetSource(exchange dia.Exchange, restDial string, wsDial string, relDB *models.RelDB, waitMilliseconds string, startBlock uint64) *UniswapV3AssetSource { 72 var ( 73 restClient *ethclient.Client 74 wsClient *ethclient.Client 75 err error 76 uas *UniswapV3AssetSource 77 assetChannel = make(chan dia.Asset) 78 doneChannel = make(chan bool) 79 ) 80 81 log.Infof("Init rest and ws client for %s.", exchange.BlockChain.Name) 82 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial)) 83 if err != nil { 84 log.Fatal("init rest client: ", err) 85 } 86 wsClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_WS", wsDial)) 87 if err != nil { 88 log.Fatal("init rest client: ", err) 89 } 90 91 var waitTime int 92 waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds) 93 waitTime, err = strconv.Atoi(waitTimeString) 94 if err != nil { 95 log.Error("could not parse wait time: ", err) 96 waitTime = 500 97 } 98 99 uas = &UniswapV3AssetSource{ 100 RestClient: restClient, 101 WsClient: wsClient, 102 relDB: relDB, 103 assetChannel: assetChannel, 104 doneChannel: doneChannel, 105 exchange: exchange, 106 startBlock: startBlock, 107 factoryContract: exchange.Contract, 108 waitTime: waitTime, 109 } 110 return uas 111 } 112 113 // getNumPairs returns the number of available pairs on Uniswap 114 func (uas *UniswapV3AssetSource) fetchAssets() { 115 116 // filter from contract created https://etherscan.io/tx/0x1e20cd6d47d7021ae7e437792823517eeadd835df09dde17ab45afd7a5df4603 117 118 log.Info("get pool creations from address: ", uas.factoryContract) 119 poolsCount := 0 120 var blocknumber int64 121 checkMap := make(map[string]struct{}) 122 _, startblock, err := uas.relDB.GetScraperIndex(uas.exchange.Name, dia.SCRAPER_TYPE_ASSETCOLLECTOR) 123 if err != nil { 124 log.Error("GetScraperIndex: ", err) 125 } else { 126 uas.startBlock = uint64(startblock) 127 } 128 129 contract, err := uniswapcontractv3.NewUniswapV3Filterer(common.HexToAddress(uas.factoryContract), uas.WsClient) 130 if err != nil { 131 log.Error(err) 132 } 133 134 currentBlockNumber, err := uas.RestClient.BlockNumber(context.Background()) 135 if err != nil { 136 log.Error("GetBlockNumber: ", err) 137 } 138 139 endblock := utils.Min(uint64(uas.startBlock)+numBlocksQuery, currentBlockNumber) 140 log.Infof("startblock -- endblock: %v -- %v", uas.startBlock, endblock) 141 142 for uas.startBlock <= currentBlockNumber { 143 poolCreated, err := contract.FilterPoolCreated( 144 &bind.FilterOpts{Start: uas.startBlock, End: &endblock}, 145 []common.Address{}, 146 []common.Address{}, 147 []*big.Int{}, 148 ) 149 if err != nil { 150 log.Error("filter pool created: ", err) 151 } 152 for poolCreated.Next() { 153 time.Sleep(time.Duration(uas.waitTime) * time.Millisecond) 154 poolsCount++ 155 log.Info("pools count: ", poolsCount) 156 blocknumber = int64(poolCreated.Event.Raw.BlockNumber) 157 // Don't repeat sending already sent assets 158 if _, ok := checkMap[poolCreated.Event.Token0.Hex()]; !ok { 159 checkMap[poolCreated.Event.Token0.Hex()] = struct{}{} 160 asset, err := uas.GetAssetFromAddress(poolCreated.Event.Token0) 161 if err != nil { 162 log.Warnf("cannot fetch asset from address %s: %v", poolCreated.Event.Token0.Hex(), err) 163 } 164 uas.assetChannel <- asset 165 } 166 if _, ok := checkMap[poolCreated.Event.Token1.Hex()]; !ok { 167 checkMap[poolCreated.Event.Token1.Hex()] = struct{}{} 168 asset, err := uas.GetAssetFromAddress(poolCreated.Event.Token1) 169 if err != nil { 170 log.Warnf("cannot fetch asset from address %s: %v", poolCreated.Event.Token1.Hex(), err) 171 } 172 uas.assetChannel <- asset 173 } 174 } 175 err = uas.relDB.SetScraperIndex(uas.exchange.Name, dia.SCRAPER_TYPE_ASSETCOLLECTOR, dia.INDEX_TYPE_BLOCKNUMBER, blocknumber) 176 if err != nil { 177 log.Error("SetScraperIndex: ", err) 178 } 179 endblock += numBlocksQuery 180 uas.startBlock += numBlocksQuery 181 } 182 183 uas.doneChannel <- true 184 } 185 186 func (uas *UniswapV3AssetSource) GetAssetFromAddress(address common.Address) (asset dia.Asset, err error) { 187 connection := uas.RestClient 188 189 var tokenContract *uniswapcontract.IERC20Caller 190 191 tokenContract, err = uniswapcontract.NewIERC20Caller(address, connection) 192 if err != nil { 193 log.Error(err) 194 } 195 196 symbol, err := tokenContract.Symbol(&bind.CallOpts{}) 197 if err != nil { 198 log.Error(err) 199 } 200 name, err := tokenContract.Name(&bind.CallOpts{}) 201 if err != nil { 202 log.Error(err) 203 } 204 decimals, err := tokenContract.Decimals(&bind.CallOpts{}) 205 if err != nil { 206 log.Error(err) 207 } 208 209 asset = dia.Asset{ 210 Symbol: symbol, 211 Name: name, 212 Address: address.Hex(), 213 Blockchain: uas.exchange.BlockChain.Name, 214 Decimals: decimals, 215 } 216 217 return 218 } 219 220 func (uas *UniswapV3AssetSource) Asset() chan dia.Asset { 221 return uas.assetChannel 222 } 223 224 func (uas *UniswapV3AssetSource) Done() chan bool { 225 return uas.doneChannel 226 }