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  }