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  }