github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/Maverick.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"
    12  	"github.com/diadata-org/diadata/pkg/dia/helpers/ethhelper"
    13  	pairfactorycontract "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/maverick/pairfactory"
    14  	poolcontract "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/maverick/pool"
    15  	models "github.com/diadata-org/diadata/pkg/model"
    16  	"github.com/diadata-org/diadata/pkg/utils"
    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  const (
    23  	factoryContractAddressDeploymentBlockEth = uint64(17210221)
    24  	defaultWaitMillis                        = "25"
    25  )
    26  
    27  type MaverickPool struct {
    28  	Token0      dia.Asset
    29  	Token1      dia.Asset
    30  	ForeignName string
    31  	Address     common.Address
    32  }
    33  
    34  type MaverickScraper struct {
    35  	RestClient                       *ethclient.Client
    36  	poolFactoryContractAddress       string
    37  	poolFactoryContractCreationBlock uint64
    38  	relDB                            *models.RelDB
    39  	datastore                        *models.DB
    40  	poolChannel                      chan dia.Pool
    41  	doneChannel                      chan bool
    42  	blockchain                       string
    43  	waitTime                         int
    44  	exchangeName                     string
    45  	pathToPools                      string
    46  }
    47  
    48  func NewMaverickScraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) (s *MaverickScraper) {
    49  
    50  	pathToPools := utils.Getenv("PATH_TO_POOLS", "")
    51  
    52  	switch exchange.Name {
    53  	case dia.MaverickExchange:
    54  		s = makeMaverickPoolScraper(exchange, pathToPools, exchange.RestAPI, relDB, datastore, defaultWaitMillis, factoryContractAddressDeploymentBlockEth)
    55  		//case dia.MaverickExchangeBNB:
    56  		//	s = makeMaverickScraper(exchange, listenByAddress, fetchPoolsFromDB, restDialEth, wsDialEth, maverickWaitMilliseconds)
    57  		//case dia.MaverickExchangeZKSync:
    58  		//	s = makeMaverickScraper(exchange, listenByAddress, fetchPoolsFromDB, restDialEth, wsDialEth, maverickWaitMilliseconds)
    59  	}
    60  
    61  	exchangeFactoryContractAddress = exchange.Contract
    62  
    63  	go func() {
    64  		s.fetchPools()
    65  	}()
    66  	return s
    67  
    68  }
    69  
    70  func makeMaverickPoolScraper(exchange dia.Exchange, pathToPools string, restDial string, relDB *models.RelDB, datastore *models.DB, waitMilliseconds string, factoryContractDeploymentBlock uint64) *MaverickScraper {
    71  	var (
    72  		restClient  *ethclient.Client
    73  		err         error
    74  		poolChannel = make(chan dia.Pool)
    75  		doneChannel = make(chan bool)
    76  		s           *MaverickScraper
    77  		waitTime    int
    78  	)
    79  
    80  	log.Infof("Init rest client for %s.", exchange.BlockChain.Name)
    81  	restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial))
    82  	if err != nil {
    83  		log.Fatal("init rest client: ", err)
    84  	}
    85  
    86  	waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds)
    87  	waitTime, err = strconv.Atoi(waitTimeString)
    88  	if err != nil {
    89  		log.Error("could not parse wait time: ", err)
    90  		waitTime = 500
    91  	}
    92  
    93  	s = &MaverickScraper{
    94  		RestClient:                       restClient,
    95  		poolFactoryContractAddress:       exchange.Contract,
    96  		poolFactoryContractCreationBlock: factoryContractDeploymentBlock,
    97  		relDB:                            relDB,
    98  		datastore:                        datastore,
    99  		poolChannel:                      poolChannel,
   100  		doneChannel:                      doneChannel,
   101  		blockchain:                       exchange.BlockChain.Name,
   102  		waitTime:                         waitTime,
   103  		exchangeName:                     exchange.Name,
   104  		pathToPools:                      pathToPools,
   105  	}
   106  	return s
   107  }
   108  
   109  func (s *MaverickScraper) fetchPools() {
   110  	if s.pathToPools != "" {
   111  
   112  		// Collect all pool addresses from json file.
   113  		poolAddresses, err := getAddressesFromConfig("liquidity-scrapers/maverick/" + s.pathToPools)
   114  		if err != nil {
   115  			log.Error("fetch pool addresses from config file: ", err)
   116  		}
   117  		numPairs := len(poolAddresses)
   118  		log.Infof("listening to %d pools: %v", numPairs, poolAddresses)
   119  
   120  		for _, pool := range poolAddresses {
   121  			time.Sleep(time.Duration(s.waitTime) * time.Millisecond)
   122  			pool, err := s.getPoolByAddress(pool)
   123  			if err != nil {
   124  				log.Errorln("Error getting pool ", pool)
   125  			}
   126  			log.Info("found pool: ", pool)
   127  			s.poolChannel <- pool
   128  		}
   129  
   130  	} else {
   131  
   132  		pools, err := s.getAllPools()
   133  		if err != nil {
   134  			log.Fatal(err)
   135  		}
   136  		log.Info("Found ", len(pools), " pools")
   137  
   138  		for _, pool := range pools {
   139  			time.Sleep(time.Duration(s.waitTime) * time.Millisecond)
   140  			log.Info("found pool: ", pool)
   141  			s.poolChannel <- pool
   142  		}
   143  	}
   144  	s.doneChannel <- true
   145  }
   146  
   147  func (s *MaverickScraper) getPoolByAddress(pairAddress common.Address) (pool dia.Pool, err error) {
   148  	var (
   149  		poolContractInstance *poolcontract.PoolCaller
   150  		token0               dia.Asset
   151  		token1               dia.Asset
   152  	)
   153  
   154  	connection := s.RestClient
   155  	poolContractInstance, err = poolcontract.NewPoolCaller(pairAddress, connection)
   156  	if err != nil {
   157  		log.Error(err)
   158  		return dia.Pool{}, err
   159  	}
   160  
   161  	// Getting tokens from pair
   162  	address0, _ := poolContractInstance.TokenA(&bind.CallOpts{})
   163  	address1, _ := poolContractInstance.TokenB(&bind.CallOpts{})
   164  
   165  	//log.Info(address0)
   166  	//log.Info(address1)
   167  	// Only fetch assets from on-chain in case they are not in our DB.
   168  	token0, err = s.relDB.GetAsset(address0.Hex(), s.blockchain)
   169  	if err != nil {
   170  		token0, err = ethhelper.ETHAddressToAsset(address0, s.RestClient, s.blockchain)
   171  		if err != nil {
   172  			return
   173  		}
   174  	}
   175  	token1, err = s.relDB.GetAsset(address1.Hex(), s.blockchain)
   176  	if err != nil {
   177  		token1, err = ethhelper.ETHAddressToAsset(address1, s.RestClient, s.blockchain)
   178  		if err != nil {
   179  			return
   180  		}
   181  	}
   182  
   183  	// Getting liquidity
   184  
   185  	balanceA, err := poolContractInstance.BinBalanceA(&bind.CallOpts{})
   186  	balanceB, err := poolContractInstance.BinBalanceB(&bind.CallOpts{})
   187  	if err != nil {
   188  		log.Error("get reserves: ", err)
   189  	}
   190  
   191  	amount0, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(balanceA), new(big.Float).SetFloat64(math.Pow10(18))).Float64()
   192  	amount1, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(balanceB), new(big.Float).SetFloat64(math.Pow10(18))).Float64()
   193  
   194  	// TO DO: Fetch timestamp using block number?
   195  	pool.Time = time.Now()
   196  
   197  	// Fill Pool type with the above data
   198  	pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{
   199  		Asset:  token0,
   200  		Volume: amount0,
   201  		Index:  uint8(0),
   202  	})
   203  	pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{
   204  		Asset:  token1,
   205  		Volume: amount1,
   206  		Index:  uint8(1),
   207  	})
   208  
   209  	// Determine USD liquidity
   210  	if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) {
   211  		s.datastore.GetPoolLiquiditiesUSD(&pool, priceCache)
   212  	}
   213  
   214  	pool.Address = pairAddress.Hex()
   215  	pool.Blockchain = dia.BlockChain{Name: s.blockchain}
   216  	pool.Exchange = dia.Exchange{Name: s.exchangeName}
   217  
   218  	return pool, nil
   219  }
   220  
   221  func (s *MaverickScraper) getAllPools() ([]dia.Pool, error) {
   222  	pools := make([]dia.Pool, 0)
   223  
   224  	var factoryContractInstance *pairfactorycontract.PairfactoryFilterer
   225  	factoryContractInstance, err := pairfactorycontract.NewPairfactoryFilterer(common.HexToAddress(s.poolFactoryContractAddress), s.RestClient)
   226  	if err != nil {
   227  		log.Error(err)
   228  		return pools, err
   229  	}
   230  
   231  	currBlock, err := s.RestClient.BlockNumber(context.Background())
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	var offset uint64 = 2500
   237  	startBlock := s.poolFactoryContractCreationBlock
   238  	var endBlock = startBlock + offset
   239  
   240  	for {
   241  		if endBlock > currBlock {
   242  			endBlock = currBlock
   243  		}
   244  		log.Infof("startblock - endblock: %v --- %v ", startBlock, endBlock)
   245  
   246  		it, err := factoryContractInstance.FilterPoolCreated(
   247  			&bind.FilterOpts{
   248  				Start: startBlock,
   249  				End:   &endBlock,
   250  			})
   251  		if err != nil {
   252  			log.Error(err)
   253  			//return pools, err
   254  			if endBlock == currBlock {
   255  				break
   256  			}
   257  
   258  			startBlock = endBlock + 1
   259  			endBlock = endBlock + offset
   260  			continue
   261  		}
   262  
   263  		for it.Next() {
   264  			pool, err := s.getPoolByAddress(it.Event.PoolAddress)
   265  			if err != nil {
   266  				log.Warn(err)
   267  			} else {
   268  				pools = append(pools, pool)
   269  			}
   270  		}
   271  		if err := it.Close(); err != nil {
   272  			log.Warn("closing iterator: ", it)
   273  		}
   274  
   275  		if endBlock == currBlock {
   276  			break
   277  		}
   278  
   279  		startBlock = endBlock + 1
   280  		endBlock = endBlock + offset
   281  	}
   282  
   283  	return pools, err
   284  }
   285  
   286  func (s *MaverickScraper) Pool() chan dia.Pool {
   287  	return s.poolChannel
   288  }
   289  
   290  func (s *MaverickScraper) Done() chan bool {
   291  	return s.doneChannel
   292  }