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

     1  package liquidityscrapers
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"math/big"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    11  	"github.com/ethereum/go-ethereum/common"
    12  	"github.com/ethereum/go-ethereum/ethclient"
    13  
    14  	"github.com/diadata-org/diadata/pkg/dia/helpers/ethhelper"
    15  	vault "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/balancerv3/vault"
    16  	vaultextension "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/balancerv3/vaultextension"
    17  	models "github.com/diadata-org/diadata/pkg/model"
    18  	"github.com/diadata-org/diadata/pkg/utils"
    19  
    20  	"github.com/diadata-org/diadata/pkg/dia"
    21  )
    22  
    23  const (
    24  	balancerV3FilterPageSize = 5000
    25  	balancerV3RestDial       = ""
    26  )
    27  
    28  type BalancerV3Scraper struct {
    29  	RestClient             *ethclient.Client
    30  	relDB                  *models.RelDB
    31  	datastore              *models.DB
    32  	poolChannel            chan dia.Pool
    33  	doneChannel            chan bool
    34  	blockchain             string
    35  	exchangeName           string
    36  	vaultContract          string
    37  	startblockPoolRegister uint64
    38  	cachedAssets           map[string]dia.Asset
    39  }
    40  
    41  // NewBalancerV2Scraper returns a Balancer V2 scraper
    42  func NewBalancerV3Scraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) *BalancerV3Scraper {
    43  	var (
    44  		restClient  *ethclient.Client
    45  		err         error
    46  		poolChannel = make(chan dia.Pool)
    47  		doneChannel = make(chan bool)
    48  		scraper     *BalancerV3Scraper
    49  	)
    50  
    51  	log.Infof("Init rest client for %s.", exchange.BlockChain.Name)
    52  	restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", balancerV3RestDial))
    53  	if err != nil {
    54  		log.Fatal("init rest client: ", err)
    55  	}
    56  
    57  	scraper = &BalancerV3Scraper{
    58  		RestClient:    restClient,
    59  		relDB:         relDB,
    60  		datastore:     datastore,
    61  		poolChannel:   poolChannel,
    62  		doneChannel:   doneChannel,
    63  		blockchain:    exchange.BlockChain.Name,
    64  		exchangeName:  exchange.Name,
    65  		vaultContract: exchange.Contract,
    66  		cachedAssets:  make(map[string]dia.Asset),
    67  	}
    68  
    69  	switch exchange.Name {
    70  	case dia.BalancerV3Exchange:
    71  		scraper.startblockPoolRegister = 21332121
    72  
    73  	}
    74  
    75  	go func() {
    76  		scraper.fetchPools()
    77  	}()
    78  
    79  	return scraper
    80  }
    81  
    82  // fetchPools collects all available pools and sends them into the pool channel.
    83  func (scraper *BalancerV3Scraper) fetchPools() {
    84  	events, err := scraper.allRegisteredPools()
    85  	if err != nil {
    86  		log.Fatal("fetch all registered pools: ", err)
    87  	}
    88  
    89  	vaultExtensionCaller, err := vaultextension.NewVaultextensionCaller(common.HexToAddress(scraper.vaultContract), scraper.RestClient)
    90  	if err != nil {
    91  		log.Fatal("vault extension caller: ", err)
    92  	}
    93  
    94  	for _, evt := range events {
    95  		poolTokens, err := vaultExtensionCaller.GetPoolData(&bind.CallOpts{}, evt.Pool)
    96  		if err != nil {
    97  			log.Warn("get pool tokens: ", err)
    98  		}
    99  		assetvolumes := scraper.extractPoolInfo(poolTokens)
   100  		pool := dia.Pool{
   101  			Exchange:     dia.Exchange{Name: scraper.exchangeName},
   102  			Blockchain:   dia.BlockChain{Name: scraper.blockchain},
   103  			Address:      evt.Pool.String(),
   104  			Assetvolumes: assetvolumes,
   105  			Time:         time.Now(),
   106  		}
   107  
   108  		// Determine USD liquidity.
   109  		if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) {
   110  			scraper.datastore.GetPoolLiquiditiesUSD(&pool, priceCache)
   111  		}
   112  
   113  		scraper.poolChannel <- pool
   114  	}
   115  	scraper.doneChannel <- true
   116  }
   117  
   118  // allRegisteredPools returns a slice of all pool creation events.
   119  func (scraper *BalancerV3Scraper) allRegisteredPools() ([]*vault.VaultPoolRegistered, error) {
   120  	var (
   121  		offset     uint64 = balancerV3FilterPageSize
   122  		startBlock uint64 = scraper.startblockPoolRegister
   123  		endBlock          = startBlock + offset
   124  		events     []*vault.VaultPoolRegistered
   125  	)
   126  
   127  	filterer, err := vault.NewVaultFilterer(common.HexToAddress(scraper.vaultContract), scraper.RestClient)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	currBlock, err := scraper.RestClient.BlockNumber(context.Background())
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	for {
   138  		if endBlock > currBlock {
   139  			endBlock = currBlock
   140  		}
   141  		log.Infof("startblock - endblock: %v --- %v ", startBlock, endBlock)
   142  
   143  		it, err := filterer.FilterPoolRegistered(&bind.FilterOpts{
   144  			Start: startBlock,
   145  			End:   &endBlock,
   146  		}, nil, nil)
   147  		if err != nil {
   148  			log.Warn("filterpoolregistered: ", err)
   149  			continue
   150  		}
   151  
   152  		for it.Next() {
   153  			events = append(events, it.Event)
   154  		}
   155  		if err := it.Close(); err != nil {
   156  			log.Warn("closing iterator: ", it)
   157  		}
   158  
   159  		if endBlock == currBlock {
   160  			break
   161  		}
   162  
   163  		startBlock = endBlock + 1
   164  		endBlock = endBlock + offset
   165  	}
   166  
   167  	return events, nil
   168  }
   169  
   170  // extractPoolInfo returns assetvolumes in the correct format for a dia.Pool.
   171  func (scraper *BalancerV3Scraper) extractPoolInfo(poolData vaultextension.PoolData) (assetvolumes []dia.AssetVolume) {
   172  	for i := range poolData.Tokens {
   173  
   174  		asset, err := scraper.relDB.GetAsset(poolData.Tokens[i].Hex(), scraper.blockchain)
   175  		if err != nil {
   176  			asset, err = ethhelper.ETHAddressToAsset(poolData.Tokens[i], scraper.RestClient, scraper.blockchain)
   177  			if err != nil {
   178  				log.Warn("cannot fetch asset from address ", poolData.Tokens[i].Hex())
   179  				continue
   180  			}
   181  		}
   182  
   183  		volume, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(poolData.BalancesRaw[i]), new(big.Float).SetFloat64(math.Pow10(int(asset.Decimals)))).Float64()
   184  
   185  		assetvolumes = append(assetvolumes, dia.AssetVolume{Asset: asset, Volume: volume, Index: uint8(i)})
   186  	}
   187  	return
   188  }
   189  
   190  func (scraper *BalancerV3Scraper) Pool() chan dia.Pool {
   191  	return scraper.poolChannel
   192  }
   193  
   194  func (scraper *BalancerV3Scraper) Done() chan bool {
   195  	return scraper.doneChannel
   196  }