github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/TraderJoeScraper.go (about)

     1  package liquidityscrapers
     2  
     3  import (
     4  	"math"
     5  	"math/big"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/diadata-org/diadata/pkg/dia"
    11  	"github.com/diadata-org/diadata/pkg/dia/helpers/ethhelper"
    12  	traderjoe "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/traderjoe2.1"
    13  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/traderjoe2.1/traderjoeILBPair"
    14  	models "github.com/diadata-org/diadata/pkg/model"
    15  	"github.com/diadata-org/diadata/pkg/utils"
    16  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    17  	"github.com/ethereum/go-ethereum/common"
    18  	"github.com/ethereum/go-ethereum/ethclient"
    19  )
    20  
    21  // TraderJoeLiquidityScraper manages the scraping of liquidity data for the Trader Joe exchange.
    22  type TraderJoeLiquidityScraper 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  	waitTime        int
    34  }
    35  
    36  // NewTraderJoeLiquidityScraper initializes a Trader Joe liquidity scraper, creating an instance of the
    37  // TraderJoeLiquidityScraper struct. It configures necessary parameters, initiates pool fetching, and returns
    38  // the initialized scraper.
    39  func NewTraderJoeLiquidityScraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) *TraderJoeLiquidityScraper {
    40  	log.Info("NewTraderJoeLiquidityScraper ", exchange.Name)
    41  	log.Info("NewTraderJoeLiquidityScraper Address ", exchange.Contract)
    42  
    43  	var tjls *TraderJoeLiquidityScraper
    44  
    45  	switch exchange.Name {
    46  	case dia.TraderJoeExchangeV2_1:
    47  		tjls = makeTraderJoeScraper(exchange, "", "", relDB, datastore, "200", uint64(17821282))
    48  	case dia.TraderJoeExchangeV2_1Arbitrum:
    49  		tjls = makeTraderJoeScraper(exchange, "", "", relDB, datastore, "200", uint64(77473199))
    50  	case dia.TraderJoeExchangeV2_1Avalanche:
    51  		tjls = makeTraderJoeScraper(exchange, "", "", relDB, datastore, "200", uint64(28371397))
    52  	case dia.TraderJoeExchangeV2_1BNB:
    53  		tjls = makeTraderJoeScraper(exchange, "", "", relDB, datastore, "200", uint64(27099340))
    54  	case dia.TraderJoeExchangeV2_2Avalanche:
    55  		tjls = makeTraderJoeScraper(exchange, "", "", relDB, datastore, "200", uint64(46536129))
    56  	}
    57  
    58  	go func() {
    59  		tjls.fetchPools()
    60  	}()
    61  	return tjls
    62  }
    63  
    64  // makeTraderJoeScraper initializes a Trader Joe liquidity scraper, creating an instance of the
    65  // TraderJoeLiquidityScraper struct with the specified configuration and parameters.
    66  func makeTraderJoeScraper(exchange dia.Exchange, restDial string, wsDial string, relDB *models.RelDB, datastore *models.DB, waitMilliSeconds string, startBlock uint64) *TraderJoeLiquidityScraper {
    67  	var (
    68  		restClient  *ethclient.Client
    69  		wsClient    *ethclient.Client
    70  		err         error
    71  		poolChannel = make(chan dia.Pool)
    72  		doneChannel = make(chan bool)
    73  		tjls        *TraderJoeLiquidityScraper
    74  	)
    75  
    76  	log.Infof("Init rest and ws client for %s.", exchange.BlockChain.Name)
    77  	restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial))
    78  	if err != nil {
    79  		log.Fatal("init rest client: ", err)
    80  	}
    81  	wsClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_WS", wsDial))
    82  	if err != nil {
    83  		log.Fatal("init ws client: ", err)
    84  	}
    85  
    86  	var waitTime int
    87  	waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliSeconds)
    88  	waitTime, err = strconv.Atoi(waitTimeString)
    89  	if err != nil {
    90  		log.Error("could not parse wait time: ", err)
    91  		waitTime = 500
    92  	}
    93  
    94  	tjls = &TraderJoeLiquidityScraper{
    95  		WsClient:        wsClient,
    96  		RestClient:      restClient,
    97  		relDB:           relDB,
    98  		datastore:       datastore,
    99  		poolChannel:     poolChannel,
   100  		doneChannel:     doneChannel,
   101  		blockchain:      exchange.BlockChain.Name,
   102  		startBlock:      startBlock,
   103  		factoryContract: exchange.Contract,
   104  		exchangeName:    exchange.Name,
   105  		waitTime:        waitTime,
   106  	}
   107  	return tjls
   108  }
   109  
   110  // fetchPools retrieves pool creation events from the Trader Joe factory contract address and processes them.
   111  func (tjls *TraderJoeLiquidityScraper) fetchPools() {
   112  
   113  	log.Info("Fetching Trader Joe LBPairCreated events...")
   114  	log.Info("Getting lb pairs creations from address: ", tjls.factoryContract)
   115  
   116  	// Initialize a count for the number of pairs processed.
   117  	pairCount := 0
   118  
   119  	// Initialize an Ethereum event filter for the Trader Joe factory contract.
   120  	contractFilter, err := traderjoe.NewTraderjoeFilterer(common.HexToAddress(tjls.factoryContract), tjls.WsClient)
   121  	if err != nil {
   122  		log.Error(err)
   123  	}
   124  
   125  	// Retrieve LBPairCreated events using the event filter.
   126  	lbPairCreated, err := contractFilter.FilterLBPairCreated(
   127  		&bind.FilterOpts{Start: tjls.startBlock},
   128  		[]common.Address{},
   129  		[]common.Address{},
   130  		[]*big.Int{},
   131  	)
   132  	if err != nil {
   133  		log.Error("filter pool created: ", err)
   134  	}
   135  
   136  	// Iterate through the LBPairCreated events.
   137  	for lbPairCreated.Next() {
   138  		pairCount++
   139  		var (
   140  			pool   dia.Pool
   141  			asset0 dia.Asset
   142  			asset1 dia.Asset
   143  		)
   144  		log.Info("pairs count: ", pairCount)
   145  		time.Sleep(time.Duration(tjls.waitTime) * time.Millisecond)
   146  
   147  		// Retrieve information about the first token of the liquidity pool.
   148  		asset0, err = tjls.relDB.GetAsset(lbPairCreated.Event.TokenX.Hex(), tjls.blockchain)
   149  		if err != nil {
   150  			// If asset information cannot be retrieved from the database, try fetching it from the Ethereum network.
   151  			asset0, err = ethhelper.ETHAddressToAsset(lbPairCreated.Event.TokenX, tjls.RestClient, tjls.blockchain)
   152  			if err != nil {
   153  				log.Warn("cannot fetch asset from address ", lbPairCreated.Event.TokenX.Hex())
   154  				continue
   155  			}
   156  		}
   157  
   158  		// Retrieve information about the second token of the liquidity pool.
   159  		asset1, err = tjls.relDB.GetAsset(lbPairCreated.Event.TokenY.Hex(), tjls.blockchain)
   160  		if err != nil {
   161  			// If asset information cannot be retrieved from the database, try fetching it from the Ethereum network.
   162  			asset1, err = ethhelper.ETHAddressToAsset(lbPairCreated.Event.TokenY, tjls.RestClient, tjls.blockchain)
   163  			if err != nil {
   164  				log.Warn("cannot fetch asset from address ", lbPairCreated.Event.TokenY.Hex())
   165  				continue
   166  			}
   167  		}
   168  
   169  		// Populate pool information.
   170  		pool.Exchange = dia.Exchange{Name: tjls.exchangeName}
   171  		pool.Blockchain = dia.BlockChain{Name: tjls.blockchain}
   172  		pool.Address = lbPairCreated.Event.LBPair.Hex()
   173  
   174  		pairFiltererContract, err := traderjoeILBPair.NewILBPairCaller(lbPairCreated.Event.LBPair, tjls.RestClient)
   175  		if err != nil {
   176  			log.Fatal(err)
   177  		}
   178  		reserves, err := pairFiltererContract.GetReserves(&bind.CallOpts{})
   179  		if err != nil {
   180  			log.Fatal("get reserves on pool ", lbPairCreated.Event.LBPair.Hex())
   181  		}
   182  
   183  		// Calculate token balances in floating-point format.
   184  		balance0, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(reserves.ReserveX), new(big.Float).SetFloat64(math.Pow10(int(asset0.Decimals)))).Float64()
   185  		balance1, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(reserves.ReserveY), new(big.Float).SetFloat64(math.Pow10(int(asset1.Decimals)))).Float64()
   186  
   187  		// Append asset volumes to the pool.
   188  		pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{Asset: asset0, Volume: balance0, Index: uint8(0)})
   189  		pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{Asset: asset1, Volume: balance1, Index: uint8(1)})
   190  
   191  		// Determine USD liquidity for the pool if both token balances meet the threshold.
   192  		if balance0 > GLOBAL_NATIVE_LIQUIDITY_THRESHOLD && balance1 > GLOBAL_NATIVE_LIQUIDITY_THRESHOLD {
   193  			tjls.datastore.GetPoolLiquiditiesUSD(&pool, priceCache)
   194  		}
   195  
   196  		// Set the timestamp for the pool.
   197  		pool.Time = time.Now()
   198  
   199  		// Send the processed pool information to the channel.
   200  		tjls.poolChannel <- pool
   201  	}
   202  
   203  	// Signal that pool retrieval and processing is complete.
   204  	tjls.doneChannel <- true
   205  }
   206  
   207  // Pool returns a channel for receiving dia.Pool instances scraped by the Trader Joe liquidity scraper.
   208  func (tjls *TraderJoeLiquidityScraper) Pool() chan dia.Pool {
   209  	return tjls.poolChannel
   210  }
   211  
   212  // Done returns a channel for signaling the completion of Trader Joe liquidity scraping.
   213  func (tjls *TraderJoeLiquidityScraper) Done() chan bool {
   214  	return tjls.doneChannel
   215  }