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

     1  package source
     2  
     3  import (
     4  	"math/big"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance"
     9  	platypusAssetABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/asset"
    10  	platypusPoolABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/pool"
    11  	platypusTokenABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/token"
    12  	"github.com/diadata-org/diadata/pkg/utils"
    13  
    14  	"github.com/diadata-org/diadata/pkg/dia"
    15  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    16  	"github.com/ethereum/go-ethereum/common"
    17  	"github.com/ethereum/go-ethereum/ethclient"
    18  )
    19  
    20  const (
    21  	platypusRestDialAvalanche = "https://api.avax.network/ext/bc/C/rpc"
    22  	platypusMasterRegV3Addr   = "0x7125B4211357d7C3a90F796c956c12c681146EbB"
    23  	platypusMasterRegV2Addr   = "0x68c5f4374228BEEdFa078e77b5ed93C28a2f713E"
    24  	platypusMasterRegV1Addr   = "0xB0523f9F473812FB195Ee49BC7d2ab9873a98044"
    25  	waitMilliseconds          = ""
    26  )
    27  
    28  type platypusRegistry struct {
    29  	Address common.Address
    30  	Version int
    31  }
    32  
    33  type PlatypusCoin struct {
    34  	Symbol   string
    35  	Decimals uint8
    36  	Address  string
    37  	Name     string
    38  }
    39  
    40  // The scraper object for Platypus Finance.
    41  type PlatypusAssetSource struct {
    42  	RestClient   *ethclient.Client
    43  	assetChannel chan dia.Asset
    44  	doneChannel  chan bool
    45  	blockchain   string
    46  	registries   []platypusRegistry
    47  	waitTime     int
    48  }
    49  
    50  // Returns a new platypus asset scraper.
    51  func NewPlatypusScraper(exchange dia.Exchange) *PlatypusAssetSource {
    52  
    53  	var (
    54  		restClient   *ethclient.Client
    55  		err          error
    56  		assetChannel = make(chan dia.Asset)
    57  		doneChannel  = make(chan bool)
    58  		pas          *PlatypusAssetSource
    59  	)
    60  
    61  	registries := []platypusRegistry{
    62  		{Version: 3, Address: common.HexToAddress(platypusMasterRegV3Addr)},
    63  		{Version: 2, Address: common.HexToAddress(platypusMasterRegV2Addr)},
    64  		{Version: 1, Address: common.HexToAddress(platypusMasterRegV1Addr)},
    65  	}
    66  
    67  	log.Infof("Init rest client for %s.", exchange.BlockChain.Name)
    68  	restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", platypusRestDialAvalanche))
    69  	if err != nil {
    70  		log.Fatal("init rest client: ", err)
    71  	}
    72  
    73  	var waitTime int
    74  	waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds)
    75  	waitTime, err = strconv.Atoi(waitTimeString)
    76  	if err != nil {
    77  		log.Error("could not parse wait time: ", err)
    78  		waitTime = 500
    79  	}
    80  	pas = &PlatypusAssetSource{
    81  		RestClient:   restClient,
    82  		assetChannel: assetChannel,
    83  		doneChannel:  doneChannel,
    84  		blockchain:   exchange.BlockChain.Name,
    85  		waitTime:     waitTime,
    86  		registries:   registries,
    87  	}
    88  	go func() {
    89  		pas.fetchAssets()
    90  	}()
    91  
    92  	return pas
    93  }
    94  
    95  func (pas *PlatypusAssetSource) fetchAssets() {
    96  	for _, registry := range pas.registries {
    97  		err := pas.fetchPools(registry)
    98  		if err != nil {
    99  			log.Error("loadPoolsAndCoins: ", err)
   100  		}
   101  	}
   102  	pas.doneChannel <- true
   103  }
   104  
   105  // Load pools and coins metadata from master registry
   106  func (scraper *PlatypusAssetSource) fetchPools(registry platypusRegistry) error {
   107  	log.Infof("loading master contract %s version %d and querying registry", registry.Address.Hex(), registry.Version)
   108  	contractMaster, err := platypusfinance.NewBaseMasterPlatypusCaller(registry.Address, scraper.RestClient)
   109  	if err != nil {
   110  		log.Error("NewBaseMasterPlatypusCaller: ", err)
   111  		return err
   112  	}
   113  
   114  	poolCount, err := contractMaster.PoolLength(&bind.CallOpts{})
   115  	if err != nil {
   116  		log.Error("PoolLength: ", err)
   117  		return err
   118  	}
   119  
   120  	for i := 0; i < int(poolCount.Int64()); i++ {
   121  		asset, errPoolInfo := contractMaster.PoolInfo(&bind.CallOpts{}, big.NewInt(int64(i)))
   122  		if errPoolInfo != nil {
   123  			log.Error("PoolInfo: ", errPoolInfo)
   124  			return err
   125  		}
   126  
   127  		errPoolData := scraper.loadPoolData(asset.LpToken.Hex())
   128  		if errPoolData != nil {
   129  			log.Errorf("loadPoolData error at asset %s: %s", asset.LpToken.Hex(), errPoolData)
   130  			return errPoolData
   131  		}
   132  
   133  	}
   134  
   135  	return err
   136  }
   137  
   138  func (scraper *PlatypusAssetSource) loadPoolData(poolAddress string) (err error) {
   139  
   140  	contractAsset, err := platypusAssetABI.NewAssetCaller(common.HexToAddress(poolAddress), scraper.RestClient)
   141  	if err != nil {
   142  		log.Error("NewAssetCaller: ", err)
   143  	}
   144  
   145  	poolContract, err := contractAsset.Pool(&bind.CallOpts{})
   146  	if err != nil {
   147  		log.Error("Pool: ", err)
   148  	}
   149  
   150  	poolCaller, err := platypusPoolABI.NewPoolCaller(poolContract, scraper.RestClient)
   151  	if err != nil {
   152  		log.Error("NewPoolCaller: ", err)
   153  	}
   154  
   155  	poolTokenAddresses, errGetTokens := poolCaller.GetTokenAddresses(&bind.CallOpts{})
   156  	if errGetTokens != nil {
   157  		symbol, err := contractAsset.Symbol(&bind.CallOpts{})
   158  		if err != nil {
   159  			log.Error("contractAsset.Symbol: ", err)
   160  		}
   161  		log.Warnf("error calling GetTokenAddresses for %s %s asset: %s", symbol, poolAddress, errGetTokens)
   162  	}
   163  
   164  	for _, c := range poolTokenAddresses {
   165  		var (
   166  			symbol      string
   167  			decimalsBig *big.Int
   168  			decimals    uint8
   169  			name        string
   170  		)
   171  
   172  		if c == common.HexToAddress("0x0000000000000000000000000000000000000000") {
   173  			continue
   174  		} else {
   175  			contractToken, err := platypusTokenABI.NewTokenCaller(c, scraper.RestClient)
   176  			if err != nil {
   177  				log.Error("NewTokenCaller: ", err)
   178  				continue
   179  			}
   180  
   181  			symbol, err = contractToken.Symbol(&bind.CallOpts{})
   182  			if err != nil {
   183  				log.Error("Symbol: ", err, c.Hex())
   184  				continue
   185  			}
   186  
   187  			decimalsBig, err = contractToken.Decimals(&bind.CallOpts{})
   188  			if err != nil {
   189  				log.Error("Decimals: ", err)
   190  				continue
   191  			}
   192  			decimals = uint8(decimalsBig.Uint64())
   193  
   194  			name, err = contractToken.Name(&bind.CallOpts{})
   195  			if err != nil {
   196  				log.Error("Name: ", err)
   197  				continue
   198  			}
   199  
   200  		}
   201  
   202  		poolAsset := dia.Asset{
   203  			Address:    c.Hex(),
   204  			Blockchain: scraper.blockchain,
   205  			Decimals:   decimals,
   206  			Symbol:     symbol,
   207  			Name:       name,
   208  		}
   209  
   210  		scraper.Asset() <- poolAsset
   211  
   212  	}
   213  	return
   214  }
   215  
   216  func (pas *PlatypusAssetSource) Asset() chan dia.Asset {
   217  	return pas.assetChannel
   218  }
   219  
   220  func (pas *PlatypusAssetSource) Done() chan bool {
   221  	return pas.doneChannel
   222  }