github.com/diadata-org/diadata@v1.4.593/pkg/dia/service/assetservice/source/curvefi.go (about)

     1  package source
     2  
     3  import (
     4  	"math/big"
     5  	"strconv"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefi"
    10  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefi/token"
    11  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefifactory"
    12  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefimeta"
    13  	"github.com/diadata-org/diadata/pkg/utils"
    14  
    15  	"github.com/diadata-org/diadata/pkg/dia"
    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  const (
    22  	curveRestDialEth      = ""
    23  	curveRestDialFantom   = ""
    24  	curveRestDialMoonbeam = ""
    25  	curveRestDialPolygon  = ""
    26  	curveRestDialArbitrum = ""
    27  
    28  	polygonWaitMilliseconds  = "500"
    29  	fantomWaitMilliseconds   = "250"
    30  	arbitrumWaitMilliseconds = "300"
    31  )
    32  
    33  type curveRegistry struct {
    34  	Address common.Address
    35  	Type    int
    36  }
    37  
    38  // CurvefiAssetSource is a curve finance scraper on a specific blockchain.
    39  type CurvefiAssetSource struct {
    40  	RestClient   *ethclient.Client
    41  	assetChannel chan dia.Asset
    42  	doneChannel  chan bool
    43  	blockchain   string
    44  	registries   []curveRegistry
    45  	waitTime     int
    46  }
    47  
    48  func NewCurvefiAssetSource(exchange dia.Exchange) *CurvefiAssetSource {
    49  
    50  	var cas *CurvefiAssetSource
    51  
    52  	switch exchange.Name {
    53  	case dia.CurveFIExchange:
    54  		basePools := curveRegistry{Type: 1, Address: common.HexToAddress(exchange.Contract)}
    55  		cryptoswapPools := curveRegistry{Type: 1, Address: common.HexToAddress("0x8F942C20D02bEfc377D41445793068908E2250D0")}
    56  		metaPools := curveRegistry{Type: 2, Address: common.HexToAddress("0xB9fC157394Af804a3578134A6585C0dc9cc990d4")}
    57  		stableSwapRegistry := curveRegistry{Type: 3, Address: common.HexToAddress("0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf")}
    58  		factoryPools := curveRegistry{Type: 3, Address: common.HexToAddress("0xF18056Bbd320E96A48e3Fbf8bC061322531aac99")}
    59  		factory2Pools := curveRegistry{Type: 3, Address: common.HexToAddress("0x4F8846Ae9380B90d2E71D5e3D042dff3E7ebb40d")}
    60  		registries := []curveRegistry{factoryPools, factory2Pools, basePools, cryptoswapPools, metaPools, stableSwapRegistry}
    61  		cas = makeCurvefiAssetSource(exchange, registries, curveRestDialEth, uniswapWaitMilliseconds)
    62  	case dia.CurveFIExchangeFantom:
    63  		exchange.Contract = ""
    64  		// basePools := curveRegistry{Type: 1, Address: common.HexToAddress(exchange.Contract)}
    65  		stableSwapFactory := curveRegistry{Type: 2, Address: common.HexToAddress("0x686d67265703D1f124c45E33d47d794c566889Ba")}
    66  		registries := []curveRegistry{stableSwapFactory}
    67  		cas = makeCurvefiAssetSource(exchange, registries, curveRestDialFantom, fantomWaitMilliseconds)
    68  
    69  	case dia.CurveFIExchangeMoonbeam:
    70  		exchange.Contract = ""
    71  		// basePools := curveRegistry{Type: 1, Address: common.HexToAddress(exchange.Contract)}
    72  		stableSwapFactory := curveRegistry{Type: 2, Address: common.HexToAddress("0x4244eB811D6e0Ef302326675207A95113dB4E1F8")}
    73  		registries := []curveRegistry{stableSwapFactory}
    74  		cas = makeCurvefiAssetSource(exchange, registries, curveRestDialMoonbeam, moonbeamWaitMilliseconds)
    75  
    76  	case dia.CurveFIExchangePolygon:
    77  		exchange.Contract = ""
    78  		// basePools := curveRegistry{Type: 1, Address: common.HexToAddress(exchange.Contract)}
    79  		stableSwapFactory := curveRegistry{Type: 2, Address: common.HexToAddress("0x722272D36ef0Da72FF51c5A65Db7b870E2e8D4ee")}
    80  		registries := []curveRegistry{stableSwapFactory}
    81  		cas = makeCurvefiAssetSource(exchange, registries, curveRestDialPolygon, polygonWaitMilliseconds)
    82  
    83  	case dia.CurveFIExchangeArbitrum:
    84  		exchange.Contract = ""
    85  		stableSwapFactory := curveRegistry{Type: 2, Address: common.HexToAddress("0xb17b674D9c5CB2e441F8e196a2f048A81355d031")}
    86  		registries := []curveRegistry{stableSwapFactory}
    87  		cas = makeCurvefiAssetSource(exchange, registries, curveRestDialArbitrum, arbitrumWaitMilliseconds)
    88  	}
    89  
    90  	go func() {
    91  		cas.fetchAssets()
    92  	}()
    93  	return cas
    94  }
    95  
    96  // makeCurvefiAssetSource returns a curve finance scraper as used in NewCurvefiScraper.
    97  func makeCurvefiAssetSource(exchange dia.Exchange, registries []curveRegistry, restDial string, waitMilliseconds string) *CurvefiAssetSource {
    98  	var (
    99  		restClient   *ethclient.Client
   100  		err          error
   101  		assetChannel = make(chan dia.Asset)
   102  		doneChannel  = make(chan bool)
   103  		cas          *CurvefiAssetSource
   104  	)
   105  
   106  	log.Infof("Init rest client for %s.", exchange.BlockChain.Name)
   107  	restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial))
   108  	if err != nil {
   109  		log.Fatal("init rest client: ", err)
   110  	}
   111  
   112  	var waitTime int
   113  	waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds)
   114  	waitTime, err = strconv.Atoi(waitTimeString)
   115  	if err != nil {
   116  		log.Error("could not parse wait time: ", err)
   117  		waitTime = 500
   118  	}
   119  	cas = &CurvefiAssetSource{
   120  		RestClient:   restClient,
   121  		assetChannel: assetChannel,
   122  		doneChannel:  doneChannel,
   123  		blockchain:   exchange.BlockChain.Name,
   124  		waitTime:     waitTime,
   125  		registries:   registries,
   126  	}
   127  
   128  	return cas
   129  }
   130  
   131  func (cas *CurvefiAssetSource) Asset() chan dia.Asset {
   132  	return cas.assetChannel
   133  }
   134  
   135  func (cas *CurvefiAssetSource) Done() chan bool {
   136  	return cas.doneChannel
   137  }
   138  
   139  func (cas *CurvefiAssetSource) fetchAssets() {
   140  	// Load pools from registries.
   141  	for _, registry := range cas.registries {
   142  		err := cas.loadPoolsAndCoins(registry)
   143  		if err != nil {
   144  			log.Error("loadPoolsAndCoins: ", err)
   145  		}
   146  	}
   147  	cas.doneChannel <- true
   148  }
   149  
   150  // contract.poolList.map(contract.GetPoolCoins(pool).)
   151  func (cas *CurvefiAssetSource) loadPoolsAndCoins(registry curveRegistry) (err error) {
   152  
   153  	if registry.Type == 1 {
   154  		log.Info("load base type pools..")
   155  		var contract *curvefi.CurvefiCaller
   156  		var poolCount *big.Int
   157  		contract, err = curvefi.NewCurvefiCaller(registry.Address, cas.RestClient)
   158  		if err != nil {
   159  			log.Error("NewCurvefiCaller: ", err)
   160  		}
   161  		poolCount, err = contract.PoolCount(&bind.CallOpts{})
   162  		if err != nil {
   163  			log.Error("PoolCount: ", err)
   164  		}
   165  		log.Info("poolCount: ", int(poolCount.Int64()))
   166  		for i := 0; i < int(poolCount.Int64()); i++ {
   167  			var pool common.Address
   168  			pool, err = contract.PoolList(&bind.CallOpts{}, big.NewInt(int64(i)))
   169  			if err != nil {
   170  				log.Error("PoolList: ", err)
   171  			}
   172  
   173  			err = cas.loadPoolData(pool.Hex(), registry)
   174  			if err != nil {
   175  				log.Info("load pool data: ", err)
   176  				// return err
   177  			}
   178  		}
   179  	}
   180  
   181  	if registry.Type == 2 || registry.Type == 3 {
   182  		log.Info("load meta type pools...")
   183  		var contract *curvefimeta.CurvefimetaCaller
   184  		var poolCount *big.Int
   185  		contract, err = curvefimeta.NewCurvefimetaCaller(registry.Address, cas.RestClient)
   186  		if err != nil {
   187  			log.Error("NewCurvefiCaller: ", err)
   188  		}
   189  
   190  		poolCount, err = contract.PoolCount(&bind.CallOpts{})
   191  		if err != nil {
   192  			log.Error("PoolCount: ", err)
   193  		}
   194  		log.Info("poolCount: ", int(poolCount.Int64()))
   195  		for i := 0; i < int(poolCount.Int64()); i++ {
   196  			var pool common.Address
   197  			pool, err = contract.PoolList(&bind.CallOpts{}, big.NewInt(int64(i)))
   198  			if err != nil {
   199  				log.Error("PoolList: ", err)
   200  			}
   201  
   202  			err = cas.loadPoolData(pool.Hex(), registry)
   203  			if err != nil {
   204  				log.Info("load pool data: ", err)
   205  				// return err
   206  			}
   207  		}
   208  	}
   209  	return err
   210  }
   211  
   212  func (cas *CurvefiAssetSource) loadPoolData(pool string, registry curveRegistry) error {
   213  	var poolCoins [8]common.Address
   214  
   215  	if registry.Type == 1 {
   216  		contract, err := curvefi.NewCurvefiCaller(registry.Address, cas.RestClient)
   217  		if err != nil {
   218  			log.Error("loadPoolData - NewCurvefiCaller: ", err)
   219  		}
   220  
   221  		poolCoins, err = contract.GetCoins(&bind.CallOpts{}, common.HexToAddress(pool))
   222  		if err != nil {
   223  			log.Error("loadPoolData - GetCoins: ", err)
   224  		}
   225  
   226  		poolName, err := contract.GetPoolName(&bind.CallOpts{}, common.HexToAddress(pool))
   227  		if err != nil {
   228  			log.Error("loadPoolData - GetPoolName: ", err)
   229  		}
   230  		log.Info("pool name: ", poolName)
   231  	}
   232  
   233  	if registry.Type == 2 {
   234  		// common.HexToAddress(curveFiMetaPoolsFactory) || factoryContract == common.HexToAddress(curveFiCryptoPoolFactory) {
   235  		contract, err := curvefimeta.NewCurvefimetaCaller(registry.Address, cas.RestClient)
   236  
   237  		if err != nil {
   238  			log.Error("loadPoolData - NewCurvefiCaller: ", err)
   239  		}
   240  
   241  		aux, err := contract.GetCoins(&bind.CallOpts{}, common.HexToAddress(pool))
   242  		if err != nil {
   243  			log.Error("loadPoolData - GetCoins: ", err)
   244  		}
   245  		// GetCoins on meta pools returns [4]common.Address instead of [8]common.Address for standard pools.
   246  		//nolint
   247  		for i, item := range aux {
   248  			poolCoins[i] = item
   249  		}
   250  
   251  	}
   252  	if registry.Type == 3 {
   253  		log.Info("pool type 3...")
   254  		contract, err := curvefifactory.NewCurvefifactoryCaller(common.HexToAddress(pool), cas.RestClient)
   255  		if err != nil {
   256  			log.Error("loadPoolData - NewCurvefiCaller: ", err)
   257  		}
   258  
   259  		var i int64
   260  		poolAssetAddress, err := contract.Coins(&bind.CallOpts{}, big.NewInt(i))
   261  		if err != nil {
   262  			log.Error("loadPoolData - GetCoins: ", err)
   263  		}
   264  		for poolAssetAddress != (common.Address{}) && i < 8 {
   265  			poolCoins[i] = poolAssetAddress
   266  			i++
   267  			poolAssetAddress, err = contract.Coins(&bind.CallOpts{}, big.NewInt(i))
   268  			if err != nil {
   269  				log.Warn("loadPoolData - GetCoins: ", err)
   270  			}
   271  		}
   272  
   273  	}
   274  
   275  	var err error
   276  	checkMap := make(map[string]struct{})
   277  
   278  	for _, c := range poolCoins {
   279  		time.Sleep(time.Duration(cas.waitTime) * time.Millisecond)
   280  		var (
   281  			coinCaller *token.TokenCaller
   282  			decimals   *big.Int
   283  			asset      dia.Asset
   284  		)
   285  		if c == common.HexToAddress("0x0000000000000000000000000000000000000000") {
   286  			continue
   287  		} else {
   288  			asset.Address = c.Hex()
   289  			coinCaller, err = token.NewTokenCaller(c, cas.RestClient)
   290  			if err != nil {
   291  				log.Error("NewTokenCaller: ", err)
   292  				continue
   293  			}
   294  			asset.Symbol, err = coinCaller.Symbol(&bind.CallOpts{})
   295  			if err != nil {
   296  				log.Error("Symbol: ", err, c.Hex())
   297  				continue
   298  			}
   299  			decimals, err = coinCaller.Decimals(&bind.CallOpts{})
   300  			if err != nil {
   301  				log.Error("Decimals: ", err)
   302  				continue
   303  			}
   304  			asset.Decimals = uint8(decimals.Int64())
   305  
   306  			asset.Name, err = coinCaller.Name(&bind.CallOpts{})
   307  			if err != nil {
   308  				log.Error("Name: ", err)
   309  				continue
   310  			}
   311  		}
   312  		asset.Blockchain = cas.blockchain
   313  
   314  		if _, ok := checkMap[asset.Address]; !ok {
   315  			if asset.Symbol != "" {
   316  				checkMap[asset.Address] = struct{}{}
   317  				cas.assetChannel <- asset
   318  			}
   319  		}
   320  
   321  	}
   322  	return err
   323  }