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

     1  package liquidityscrapers
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"math"
     7  	"math/big"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefi/token"
    12  	models "github.com/diadata-org/diadata/pkg/model"
    13  	"github.com/diadata-org/diadata/pkg/utils"
    14  
    15  	"github.com/diadata-org/diadata/pkg/dia"
    16  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/ConverterTypeFour"
    17  	ConvertertypeOne "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/ConverterTypeOne"
    18  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/ConverterTypeThree"
    19  
    20  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/ConverterTypeZero"
    21  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/ethclient"
    24  )
    25  
    26  type BancorPoolScraper struct {
    27  	RestClient   *ethclient.Client
    28  	datastore    *models.DB
    29  	poolChannel  chan dia.Pool
    30  	doneChannel  chan bool
    31  	blockchain   string
    32  	exchangeName string
    33  	pool         string
    34  }
    35  
    36  type BancorPool struct {
    37  	Reserves []struct {
    38  		DltID   string `json:"dlt_id"`
    39  		Symbol  string `json:"symbol"`
    40  		Name    string `json:"name"`
    41  		Balance struct {
    42  			Usd string `json:"usd"`
    43  		} `json:"balance"`
    44  		Weight int `json:"weight"`
    45  		Price  struct {
    46  			Usd string `json:"usd"`
    47  		} `json:"price"`
    48  		Price24HAgo struct {
    49  			Usd string `json:"usd"`
    50  		} `json:"price_24h_ago"`
    51  		Volume24H struct {
    52  			Usd  string `json:"usd"`
    53  			Base string `json:"base"`
    54  		} `json:"volume_24h"`
    55  	} `json:"reserves"`
    56  	DltType        string `json:"dlt_type"`
    57  	DltID          string `json:"dlt_id"`
    58  	Type           int    `json:"type"`
    59  	Version        int    `json:"version"`
    60  	Symbol         string `json:"symbol"`
    61  	Name           string `json:"name"`
    62  	Supply         string `json:"supply"`
    63  	ConverterDltID string `json:"converter_dlt_id"`
    64  	ConversionFee  string `json:"conversion_fee"`
    65  	Liquidity      struct {
    66  		Usd string `json:"usd"`
    67  	} `json:"liquidity"`
    68  	Volume24H struct {
    69  		Usd string `json:"usd"`
    70  	} `json:"volume_24h"`
    71  	Fees24H struct {
    72  		Usd string `json:"usd"`
    73  	} `json:"fees_24h"`
    74  }
    75  
    76  type BancorPools struct {
    77  	Data      []BancorPool `json:"data"`
    78  	Timestamp struct {
    79  		Ethereum struct {
    80  			Block     int   `json:"block"`
    81  			Timestamp int64 `json:"timestamp"`
    82  		} `json:"ethereum"`
    83  	} `json:"timestamp"`
    84  }
    85  
    86  func NewBancorPoolScraper(exchange dia.Exchange, datastore *models.DB) *BancorPoolScraper {
    87  	var (
    88  		restClient  *ethclient.Client
    89  		err         error
    90  		poolChannel = make(chan dia.Pool)
    91  		doneChannel = make(chan bool)
    92  		scraper     *BancorPoolScraper
    93  		pool        = "0xC0205e203F423Bcd8B2a4d6f8C8A154b0Aa60F19"
    94  	)
    95  
    96  	log.Infof("Init rest client for %s.", exchange.BlockChain.Name)
    97  	restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", curveRestDial))
    98  	if err != nil {
    99  		log.Fatal("init rest client: ", err)
   100  	}
   101  
   102  	scraper = &BancorPoolScraper{
   103  		RestClient:   restClient,
   104  		datastore:    datastore,
   105  		poolChannel:  poolChannel,
   106  		doneChannel:  doneChannel,
   107  		blockchain:   exchange.BlockChain.Name,
   108  		exchangeName: exchange.Name,
   109  		pool:         pool,
   110  	}
   111  
   112  	go func() {
   113  		scraper.fetchPools()
   114  		scraper.doneChannel <- true
   115  	}()
   116  
   117  	return scraper
   118  }
   119  
   120  func (scraper *BancorPoolScraper) readPools() ([]BancorPool, error) {
   121  	var bpools BancorPools
   122  	pairs, _, err := utils.GetRequest("https://api-v2.bancor.network/pools")
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	err = json.Unmarshal(pairs, &bpools)
   127  	if err != nil {
   128  		log.Error("Error reading json", err)
   129  
   130  	}
   131  	return bpools.Data, nil
   132  
   133  }
   134  
   135  func (scraper *BancorPoolScraper) fetchpooltokenaddress(pool BancorPool) (address []common.Address, balances []*big.Int, err error) {
   136  
   137  	switch pool.Type {
   138  	case 0:
   139  		{
   140  			address, balances, err = scraper.ConverterTypeZero(common.HexToAddress(pool.ConverterDltID))
   141  			if err != nil {
   142  				log.Errorln("Error getting Address", err)
   143  			}
   144  
   145  		}
   146  	case 1:
   147  		{
   148  			address, balances, err = scraper.ConverterTypeOne(common.HexToAddress(pool.ConverterDltID))
   149  			if err != nil {
   150  				log.Errorln("Error getting Address", err)
   151  			}
   152  		}
   153  	case 3:
   154  		{
   155  			address, balances, err = scraper.ConverterTypeThree(common.HexToAddress(pool.ConverterDltID))
   156  			if err != nil {
   157  				log.Errorln("Error getting Address", err)
   158  			}
   159  		}
   160  	case 4:
   161  		{
   162  			address, balances, err = scraper.ConverterTypeFour(common.HexToAddress(pool.ConverterDltID))
   163  			if err != nil {
   164  				log.Errorln("Error getting Address", err)
   165  			}
   166  		}
   167  	default:
   168  		err = errors.New("type not available")
   169  	}
   170  
   171  	return
   172  }
   173  
   174  func (scraper *BancorPoolScraper) loadPoolData(poolCoinAddresses []common.Address, poolBalances []*big.Int, poolAddress common.Address) (pool dia.Pool) {
   175  
   176  	var (
   177  		poolAssets []dia.Asset
   178  	)
   179  
   180  	var err error
   181  	for _, c := range poolCoinAddresses {
   182  		var (
   183  			coinCaller  *token.TokenCaller
   184  			symbol      string
   185  			decimals    uint8
   186  			decimalsBig *big.Int
   187  			name        string
   188  		)
   189  		if c == common.HexToAddress("0x0000000000000000000000000000000000000000") {
   190  			continue
   191  		} else if c == common.HexToAddress("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") {
   192  			symbol = "ETH"
   193  			decimals = uint8(18)
   194  			name = "Ether"
   195  			c = common.HexToAddress("0x0000000000000000000000000000000000000000")
   196  		} else {
   197  			log.Infoln("Getting token details for : ", c)
   198  
   199  			coinCaller, err = token.NewTokenCaller(c, scraper.RestClient)
   200  			if err != nil {
   201  				log.Error("NewTokenCaller: ", err)
   202  				continue
   203  			}
   204  			symbol, err = coinCaller.Symbol(&bind.CallOpts{})
   205  			if err != nil {
   206  				log.Error("Symbol: ", err, c.Hex())
   207  				continue
   208  			}
   209  			decimalsBig, err = coinCaller.Decimals(&bind.CallOpts{})
   210  			if err != nil {
   211  				log.Error("Decimals: ", err)
   212  				continue
   213  			}
   214  			decimals = uint8(decimalsBig.Uint64())
   215  			name, err = coinCaller.Name(&bind.CallOpts{})
   216  			if err != nil {
   217  				log.Error("Name: ", err)
   218  				continue
   219  			}
   220  		}
   221  		log.Info(symbol, " ", decimals, " ", "'", name, "'", " ", c)
   222  
   223  		poolAssets = append(poolAssets, dia.Asset{
   224  			Address:    c.Hex(),
   225  			Blockchain: scraper.blockchain,
   226  			Decimals:   decimals,
   227  			Symbol:     symbol,
   228  			Name:       name,
   229  		})
   230  
   231  	}
   232  
   233  	for i := range poolAssets {
   234  		var volume float64
   235  		if poolBalances[i] != nil {
   236  			volume, _ = new(big.Float).Quo(big.NewFloat(0).SetInt(poolBalances[i]), new(big.Float).SetFloat64(math.Pow10(int(poolAssets[i].Decimals)))).Float64()
   237  
   238  		}
   239  		pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{
   240  			Asset:  poolAssets[i],
   241  			Volume: volume,
   242  		})
   243  	}
   244  	pool.Exchange = dia.Exchange{Name: scraper.exchangeName}
   245  	pool.Blockchain = dia.BlockChain{Name: scraper.blockchain}
   246  	pool.Address = poolAddress.Hex()
   247  
   248  	// Determine USD liquidity.
   249  	if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) {
   250  		scraper.datastore.GetPoolLiquiditiesUSD(&pool, priceCache)
   251  	}
   252  
   253  	pool.Time = time.Now()
   254  
   255  	return pool
   256  }
   257  
   258  func (scraper *BancorPoolScraper) ConverterTypeZero(address common.Address) (tokenAddress []common.Address, poolBalances []*big.Int, err error) {
   259  
   260  	contract, err := ConverterTypeZero.NewConverterTypeZeroCaller(address, scraper.RestClient)
   261  	if err != nil {
   262  		return
   263  	}
   264  
   265  	tokenCount, err := contract.ConnectorTokenCount(&bind.CallOpts{})
   266  	if err != nil {
   267  		return
   268  	}
   269  
   270  	var i uint16
   271  
   272  	for i = 0; i < tokenCount; i++ {
   273  		token1, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(int64(i)))
   274  		if err != nil {
   275  			log.Errorln("Error", err)
   276  		}
   277  		tokenAddress = append(tokenAddress, token1)
   278  
   279  		reserve, err := contract.GetReserveBalance(&bind.CallOpts{}, tokenAddress[int64(i)])
   280  		if err != nil {
   281  			log.Errorln("Error", err)
   282  		}
   283  		poolBalances = append(poolBalances, reserve)
   284  	}
   285  
   286  	return
   287  
   288  }
   289  
   290  func (scraper *BancorPoolScraper) ConverterTypeOne(address common.Address) (tokenAddress []common.Address, poolBalances []*big.Int, err error) {
   291  
   292  	contract, err := ConvertertypeOne.NewConvertertypeOne(address, scraper.RestClient)
   293  	if err != nil {
   294  		return
   295  	}
   296  
   297  	tokenCount, err := contract.ConnectorTokenCount(&bind.CallOpts{})
   298  	if err != nil {
   299  		return
   300  	}
   301  
   302  	var i uint16
   303  
   304  	for i = 0; i < tokenCount; i++ {
   305  		token1, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(int64(i)))
   306  		if err != nil {
   307  			log.Errorln("Error", err)
   308  		}
   309  		tokenAddress = append(tokenAddress, token1)
   310  
   311  		reserve, err := contract.ReserveBalance(&bind.CallOpts{}, tokenAddress[int64(i)])
   312  		if err != nil {
   313  			log.Errorln("Error", err)
   314  		}
   315  		poolBalances = append(poolBalances, reserve)
   316  	}
   317  
   318  	return
   319  
   320  }
   321  
   322  func (scraper *BancorPoolScraper) ConverterTypeThree(address common.Address) (tokenAddress []common.Address, poolBalances []*big.Int, err error) {
   323  
   324  	contract, err := ConverterTypeThree.NewConverterTypeThree(address, scraper.RestClient)
   325  	if err != nil {
   326  		return
   327  	}
   328  
   329  	tokenCount, err := contract.ConnectorTokenCount(&bind.CallOpts{})
   330  	if err != nil {
   331  		return
   332  	}
   333  
   334  	var i uint16
   335  
   336  	for i = 0; i < tokenCount; i++ {
   337  		token1, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(int64(i)))
   338  		if err != nil {
   339  			log.Errorln("Error", err)
   340  		}
   341  		tokenAddress = append(tokenAddress, token1)
   342  
   343  		reserve, err := contract.ReserveBalance(&bind.CallOpts{}, tokenAddress[int64(i)])
   344  		if err != nil {
   345  			log.Errorln("Error", err)
   346  		}
   347  		poolBalances = append(poolBalances, reserve)
   348  	}
   349  
   350  	return
   351  
   352  }
   353  
   354  func (scraper *BancorPoolScraper) ConverterTypeFour(address common.Address) (tokenAddress []common.Address, poolBalances []*big.Int, err error) {
   355  
   356  	contract, err := ConverterTypeFour.NewConverterTypeFour(address, scraper.RestClient)
   357  	if err != nil {
   358  		return
   359  	}
   360  
   361  	tokenCount, err := contract.ConnectorTokenCount(&bind.CallOpts{})
   362  	if err != nil {
   363  		return
   364  	}
   365  
   366  	var i uint16
   367  
   368  	for i = 0; i < tokenCount; i++ {
   369  		token1, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(int64(i)))
   370  		if err != nil {
   371  			log.Errorln("Error", err)
   372  		}
   373  		tokenAddress = append(tokenAddress, token1)
   374  
   375  		reserve, err := contract.GetReserveBalance(&bind.CallOpts{}, tokenAddress[int64(i)])
   376  		if err != nil {
   377  			log.Errorln("Error", err)
   378  		}
   379  		poolBalances = append(poolBalances, reserve)
   380  	}
   381  
   382  	return
   383  
   384  }
   385  
   386  // fetchPools collects all available pools and sends them into the pool channel.
   387  func (scraper *BancorPoolScraper) fetchPools() {
   388  
   389  	bpools, err := scraper.readPools()
   390  	if err != nil {
   391  		log.Error("Couldn't obtain Bancor product ids:", err)
   392  	}
   393  
   394  	for _, bpool := range bpools {
   395  
   396  		poolassetaddress, balances, err := scraper.fetchpooltokenaddress(bpool)
   397  		if err != nil {
   398  			log.Errorln("error getting pool detal", err)
   399  			continue
   400  		}
   401  
   402  		pool := scraper.loadPoolData(poolassetaddress, balances, common.HexToAddress(bpool.ConverterDltID))
   403  		scraper.poolChannel <- pool
   404  
   405  	}
   406  
   407  }
   408  
   409  func (scraper *BancorPoolScraper) Pool() chan dia.Pool {
   410  	return scraper.poolChannel
   411  }
   412  
   413  func (scraper *BancorPoolScraper) Done() chan bool {
   414  	return scraper.doneChannel
   415  }