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

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