github.com/diadata-org/diadata@v1.4.593/pkg/dia/service/assetservice/source/uniswap.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/uniswap"
    10  	models "github.com/diadata-org/diadata/pkg/model"
    11  
    12  	"github.com/diadata-org/diadata/pkg/dia"
    13  	"github.com/diadata-org/diadata/pkg/utils"
    14  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    15  	"github.com/ethereum/go-ethereum/common"
    16  	"github.com/ethereum/go-ethereum/ethclient"
    17  )
    18  
    19  type UniswapPair struct {
    20  	Token0      dia.Asset
    21  	Token1      dia.Asset
    22  	ForeignName string
    23  	Address     common.Address
    24  }
    25  
    26  const (
    27  	restDial              = ""
    28  	restDialBase          = ""
    29  	restDialBSC           = ""
    30  	restDialPolygon       = ""
    31  	restDialCelo          = ""
    32  	restDialFantom        = ""
    33  	restDialMoonriver     = ""
    34  	restDialAurora        = ""
    35  	restDialArbitrum      = ""
    36  	restDialMetis         = ""
    37  	restDialAvalanche     = ""
    38  	restDialTelos         = ""
    39  	restDialEvmos         = "https://evmos-evm.publicnode.com"
    40  	restDialAstar         = ""
    41  	restDialMoonbeam      = ""
    42  	restDialWanchain      = ""
    43  	restDialUnrealTestnet = ""
    44  	restDialUnreal        = ""
    45  	restDialLinea         = ""
    46  
    47  	uniswapWaitMilliseconds     = "25"
    48  	baseWaitMilliseconds        = "200"
    49  	sushiswapWaitMilliseconds   = "100"
    50  	pancakeswapWaitMilliseconds = "100"
    51  	dfynWaitMilliseconds        = "100"
    52  	ubeswapWaitMilliseconds     = "200"
    53  	spookyswapWaitMilliseconds  = "200"
    54  	spiritswapWaitMilliseconds  = "200"
    55  	solarbeamWaitMilliseconds   = "200"
    56  	trisolarisWaitMilliseconds  = "200"
    57  	metisWaitMilliseconds       = "200"
    58  	moonriverWaitMilliseconds   = "500"
    59  	avalancheWaitMilliseconds   = "200"
    60  	telosWaitMilliseconds       = "400"
    61  	evmosWaitMilliseconds       = "400"
    62  	astarWaitMilliseconds       = "1000"
    63  	moonbeamWaitMilliseconds    = "1000"
    64  	wanchainWaitMilliseconds    = "1000"
    65  )
    66  
    67  type UniswapAssetSource struct {
    68  	RestClient   *ethclient.Client
    69  	relDB        *models.RelDB
    70  	assetChannel chan dia.Asset
    71  	doneChannel  chan bool
    72  	exchange     dia.Exchange
    73  	waitTime     int
    74  }
    75  
    76  var exchangeFactoryContractAddress string
    77  
    78  func NewUniswapAssetSource(exchange dia.Exchange, relDB *models.RelDB) (uas *UniswapAssetSource) {
    79  
    80  	switch exchange.Name {
    81  	case dia.UniswapExchange:
    82  		uas = makeUniswapAssetSource(exchange, restDial, relDB, uniswapWaitMilliseconds)
    83  	case dia.SushiSwapExchange:
    84  		uas = makeUniswapAssetSource(exchange, restDial, relDB, sushiswapWaitMilliseconds)
    85  	case dia.SushiSwapExchangeArbitrum:
    86  		uas = makeUniswapAssetSource(exchange, restDialArbitrum, relDB, sushiswapWaitMilliseconds)
    87  	case dia.SushiSwapExchangeFantom:
    88  		uas = makeUniswapAssetSource(exchange, restDialFantom, relDB, sushiswapWaitMilliseconds)
    89  	case dia.SushiSwapExchangePolygon:
    90  		uas = makeUniswapAssetSource(exchange, restDialPolygon, relDB, sushiswapWaitMilliseconds)
    91  	case dia.ApeswapExchange:
    92  		uas = makeUniswapAssetSource(exchange, restDialBSC, relDB, sushiswapWaitMilliseconds)
    93  	case dia.CamelotExchange:
    94  		uas = makeUniswapAssetSource(exchange, restDialArbitrum, relDB, sushiswapWaitMilliseconds)
    95  	case dia.PanCakeSwap:
    96  		uas = makeUniswapAssetSource(exchange, restDialBSC, relDB, pancakeswapWaitMilliseconds)
    97  	case dia.DfynNetwork:
    98  		uas = makeUniswapAssetSource(exchange, restDialPolygon, relDB, dfynWaitMilliseconds)
    99  	case dia.QuickswapExchange:
   100  		uas = makeUniswapAssetSource(exchange, restDialPolygon, relDB, dfynWaitMilliseconds)
   101  	case dia.UbeswapExchange:
   102  		uas = makeUniswapAssetSource(exchange, restDialCelo, relDB, ubeswapWaitMilliseconds)
   103  	case dia.SpookyswapExchange:
   104  		uas = makeUniswapAssetSource(exchange, restDialFantom, relDB, spookyswapWaitMilliseconds)
   105  	case dia.SpiritswapExchange:
   106  		uas = makeUniswapAssetSource(exchange, restDialFantom, relDB, spiritswapWaitMilliseconds)
   107  	case dia.SolarbeamExchange:
   108  		uas = makeUniswapAssetSource(exchange, restDialMoonriver, relDB, solarbeamWaitMilliseconds)
   109  	case dia.TrisolarisExchange:
   110  		uas = makeUniswapAssetSource(exchange, restDialAurora, relDB, trisolarisWaitMilliseconds)
   111  	case dia.NetswapExchange:
   112  		uas = makeUniswapAssetSource(exchange, restDialMetis, relDB, metisWaitMilliseconds)
   113  	case dia.HuckleberryExchange:
   114  		uas = makeUniswapAssetSource(exchange, restDialMoonriver, relDB, moonriverWaitMilliseconds)
   115  	case dia.TraderJoeExchange:
   116  		uas = makeUniswapAssetSource(exchange, restDialAvalanche, relDB, avalancheWaitMilliseconds)
   117  	case dia.PangolinExchange:
   118  		uas = makeUniswapAssetSource(exchange, restDialAvalanche, relDB, avalancheWaitMilliseconds)
   119  	case dia.TethysExchange:
   120  		uas = makeUniswapAssetSource(exchange, restDialMetis, relDB, metisWaitMilliseconds)
   121  	case dia.HermesExchange:
   122  		uas = makeUniswapAssetSource(exchange, restDialMetis, relDB, metisWaitMilliseconds)
   123  	case dia.OmniDexExchange:
   124  		uas = makeUniswapAssetSource(exchange, restDialTelos, relDB, telosWaitMilliseconds)
   125  	case dia.DiffusionExchange:
   126  		uas = makeUniswapAssetSource(exchange, restDialEvmos, relDB, evmosWaitMilliseconds)
   127  	case dia.ArthswapExchange:
   128  		uas = makeUniswapAssetSource(exchange, restDialAstar, relDB, astarWaitMilliseconds)
   129  	case dia.StellaswapExchange:
   130  		uas = makeUniswapAssetSource(exchange, restDialMoonbeam, relDB, moonbeamWaitMilliseconds)
   131  	case dia.WanswapExchange:
   132  		uas = makeUniswapAssetSource(exchange, restDialWanchain, relDB, wanchainWaitMilliseconds)
   133  	case dia.PearlfiExchangeTestnet:
   134  		uas = makeUniswapAssetSource(exchange, restDialUnrealTestnet, relDB, wanchainWaitMilliseconds)
   135  	case dia.PearlfiExchange:
   136  		uas = makeUniswapAssetSource(exchange, restDialUnreal, relDB, wanchainWaitMilliseconds)
   137  	case dia.PearlfiStableswapExchange:
   138  		uas = makeUniswapAssetSource(exchange, restDialUnreal, relDB, wanchainWaitMilliseconds)
   139  	case dia.RamsesV1Exchange:
   140  		uas = makeUniswapAssetSource(exchange, restDialArbitrum, relDB, wanchainWaitMilliseconds)
   141  	case dia.NileV1Exchange:
   142  		uas = makeUniswapAssetSource(exchange, restDialLinea, relDB, wanchainWaitMilliseconds)
   143  	case dia.ThenaExchange:
   144  		uas = makeUniswapAssetSource(exchange, restDialBSC, relDB, sushiswapWaitMilliseconds)
   145  	case dia.UniswapExchangeBase:
   146  		uas = makeUniswapAssetSource(exchange, restDialBase, relDB, baseWaitMilliseconds)
   147  	}
   148  
   149  	exchangeFactoryContractAddress = exchange.Contract
   150  
   151  	go func() {
   152  		uas.fetchAssets()
   153  	}()
   154  	return uas
   155  
   156  }
   157  
   158  // makeUniswapAssetSource returns an asset source as used in NewUniswapAssetSource.
   159  func makeUniswapAssetSource(exchange dia.Exchange, restDial string, relDB *models.RelDB, waitMilliseconds string) *UniswapAssetSource {
   160  	var (
   161  		restClient   *ethclient.Client
   162  		err          error
   163  		assetChannel = make(chan dia.Asset)
   164  		doneChannel  = make(chan bool)
   165  		uas          *UniswapAssetSource
   166  	)
   167  
   168  	log.Infof("Init rest client for %s.", exchange.BlockChain.Name)
   169  	restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial))
   170  	if err != nil {
   171  		log.Fatal("init rest client: ", err)
   172  	}
   173  	var waitTime int
   174  	waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds)
   175  	waitTime, err = strconv.Atoi(waitTimeString)
   176  	if err != nil {
   177  		log.Error("could not parse wait time: ", err)
   178  		waitTime = 500
   179  	}
   180  	uas = &UniswapAssetSource{
   181  		RestClient:   restClient,
   182  		relDB:        relDB,
   183  		assetChannel: assetChannel,
   184  		doneChannel:  doneChannel,
   185  		exchange:     exchange,
   186  		waitTime:     waitTime,
   187  	}
   188  	return uas
   189  }
   190  
   191  func (uas *UniswapAssetSource) Asset() chan dia.Asset {
   192  	return uas.assetChannel
   193  }
   194  
   195  func (uas *UniswapAssetSource) Done() chan bool {
   196  	return uas.doneChannel
   197  }
   198  
   199  func (uas *UniswapAssetSource) getNumPairs() (int, error) {
   200  	var contract *uniswap.IUniswapV2FactoryCaller
   201  	contract, err := uniswap.NewIUniswapV2FactoryCaller(common.HexToAddress(exchangeFactoryContractAddress), uas.RestClient)
   202  	if err != nil {
   203  		log.Error(err)
   204  	}
   205  	numPairs, err := contract.AllPairsLength(&bind.CallOpts{})
   206  	return int(numPairs.Int64()), err
   207  }
   208  
   209  func (uas *UniswapAssetSource) fetchAssets() {
   210  
   211  	numPairs, err := uas.getNumPairs()
   212  	if err != nil {
   213  		log.Fatal(err)
   214  	}
   215  	log.Info("Found ", numPairs, " pairs")
   216  	checkMap := make(map[string]struct{})
   217  
   218  	_, index, err := uas.relDB.GetScraperIndex(uas.exchange.Name, dia.SCRAPER_TYPE_ASSETCOLLECTOR)
   219  	if err != nil {
   220  		log.Error("GetScraperIndex: ", err)
   221  	}
   222  	log.Info("Start at index ", index)
   223  
   224  	for index < int64(numPairs) {
   225  		log.Info("index: ", index)
   226  		time.Sleep(time.Duration(uas.waitTime) * time.Millisecond)
   227  		pair, err := uas.GetPairByID(index)
   228  		if err != nil {
   229  			log.Errorln("Error getting pair with ID ", index)
   230  		}
   231  		asset0 := pair.Token0
   232  		asset1 := pair.Token1
   233  		asset0.Blockchain = uas.exchange.BlockChain.Name
   234  		asset1.Blockchain = uas.exchange.BlockChain.Name
   235  		// Don't repeat sending already sent assets
   236  		if _, ok := checkMap[asset0.Address]; !ok {
   237  			if asset0.Symbol != "" {
   238  				checkMap[asset0.Address] = struct{}{}
   239  				uas.assetChannel <- asset0
   240  			}
   241  		}
   242  		if _, ok := checkMap[asset1.Address]; !ok {
   243  			if asset1.Symbol != "" {
   244  				checkMap[asset1.Address] = struct{}{}
   245  				uas.assetChannel <- asset1
   246  			}
   247  		}
   248  		index++
   249  	}
   250  	err = uas.relDB.SetScraperIndex(uas.exchange.Name, dia.SCRAPER_TYPE_ASSETCOLLECTOR, dia.INDEX_TYPE_INDEX, index)
   251  	if err != nil {
   252  		log.Error("SetScraperIndex: ", err)
   253  	}
   254  	uas.doneChannel <- true
   255  }
   256  
   257  // GetPairByID returns the UniswapPair with the integer id @num
   258  func (uas *UniswapAssetSource) GetPairByID(num int64) (UniswapPair, error) {
   259  	var contract *uniswap.IUniswapV2FactoryCaller
   260  	contract, err := uniswap.NewIUniswapV2FactoryCaller(common.HexToAddress(exchangeFactoryContractAddress), uas.RestClient)
   261  	if err != nil {
   262  		log.Error(err)
   263  		return UniswapPair{}, err
   264  	}
   265  	numToken := big.NewInt(num)
   266  	pairAddress, err := contract.AllPairs(&bind.CallOpts{}, numToken)
   267  	if err != nil {
   268  		log.Error(err)
   269  		return UniswapPair{}, err
   270  	}
   271  
   272  	pair, err := uas.GetPairByAddress(pairAddress)
   273  	if err != nil {
   274  		log.Error(err)
   275  		return UniswapPair{}, err
   276  	}
   277  	// log.Infof("Get pair with ID %v: %v", num, pair)
   278  	return pair, err
   279  }
   280  
   281  func (uas *UniswapAssetSource) GetPairByAddress(pairAddress common.Address) (pair UniswapPair, err error) {
   282  	connection := uas.RestClient
   283  	var pairContract *uniswap.IUniswapV2PairCaller
   284  	pairContract, err = uniswap.NewIUniswapV2PairCaller(pairAddress, connection)
   285  	if err != nil {
   286  		log.Error(err)
   287  		return UniswapPair{}, err
   288  	}
   289  
   290  	// Getting tokens from pair ---------------------
   291  	address0, _ := pairContract.Token0(&bind.CallOpts{})
   292  	address1, _ := pairContract.Token1(&bind.CallOpts{})
   293  	var token0Contract *uniswap.IERC20Caller
   294  	var token1Contract *uniswap.IERC20Caller
   295  	token0Contract, err = uniswap.NewIERC20Caller(address0, connection)
   296  	if err != nil {
   297  		log.Error(err)
   298  	}
   299  	token1Contract, err = uniswap.NewIERC20Caller(address1, connection)
   300  	if err != nil {
   301  		log.Error(err)
   302  	}
   303  	symbol0, err := token0Contract.Symbol(&bind.CallOpts{})
   304  	if err != nil {
   305  		log.Error(err)
   306  	}
   307  	symbol1, err := token1Contract.Symbol(&bind.CallOpts{})
   308  	if err != nil {
   309  		log.Error(err)
   310  	}
   311  	decimals0, err := token0Contract.Decimals(&bind.CallOpts{})
   312  	if err != nil {
   313  		log.Error(err)
   314  	}
   315  	decimals1, err := token1Contract.Decimals(&bind.CallOpts{})
   316  	if err != nil {
   317  		log.Error(err)
   318  	}
   319  
   320  	name0, err := uas.GetName(address0)
   321  	if err != nil {
   322  		log.Error(err)
   323  		return UniswapPair{}, err
   324  	}
   325  	name1, err := uas.GetName(address1)
   326  	if err != nil {
   327  		log.Error(err)
   328  		return UniswapPair{}, err
   329  	}
   330  	token0 := dia.Asset{
   331  		Address:  address0.Hex(),
   332  		Symbol:   symbol0,
   333  		Name:     name0,
   334  		Decimals: decimals0,
   335  	}
   336  	token1 := dia.Asset{
   337  		Address:  address1.Hex(),
   338  		Symbol:   symbol1,
   339  		Name:     name1,
   340  		Decimals: decimals1,
   341  	}
   342  	pair.Token0 = token0
   343  	pair.Token1 = token1
   344  
   345  	return pair, nil
   346  }
   347  
   348  // GetDecimals returns the decimals of the token with address @tokenAddress
   349  func (uas *UniswapAssetSource) GetDecimals(tokenAddress common.Address) (decimals uint8, err error) {
   350  
   351  	var contract *uniswap.IERC20Caller
   352  	contract, err = uniswap.NewIERC20Caller(tokenAddress, uas.RestClient)
   353  	if err != nil {
   354  		log.Error(err)
   355  		return
   356  	}
   357  	decimals, err = contract.Decimals(&bind.CallOpts{})
   358  
   359  	return
   360  }
   361  
   362  func (uas *UniswapAssetSource) GetName(tokenAddress common.Address) (name string, err error) {
   363  
   364  	var contract *uniswap.IERC20Caller
   365  	contract, err = uniswap.NewIERC20Caller(tokenAddress, uas.RestClient)
   366  	if err != nil {
   367  		log.Error(err)
   368  		return
   369  	}
   370  	name, err = contract.Name(&bind.CallOpts{})
   371  
   372  	return
   373  }