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  }