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

     1  package scrapers
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"math/big"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance"
    15  	platypusAssetABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/asset"
    16  	platypusPoolABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/pool"
    17  	platypusTokenABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/token"
    18  	models "github.com/diadata-org/diadata/pkg/model"
    19  	"github.com/diadata-org/diadata/pkg/utils"
    20  
    21  	"github.com/diadata-org/diadata/pkg/dia"
    22  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/ethclient"
    25  )
    26  
    27  const (
    28  	// 30 days of blocks (avgBlocktimeTime = 2.5)
    29  	platypusLookBackBlocks  = (60 / 2.5) * 60 * 24 * 30
    30  	platypusRestDialEth     = "https://api.avax.network/ext/bc/C/rpc"
    31  	platypusWsDialEth       = "wss://api.avax.network/ext/bc/C/ws"
    32  	platypusMasterRegV3Addr = "0x7125B4211357d7C3a90F796c956c12c681146EbB"
    33  	platypusMasterRegV2Addr = "0x68c5f4374228BEEdFa078e77b5ed93C28a2f713E"
    34  	platypusMasterRegV1Addr = "0xB0523f9F473812FB195Ee49BC7d2ab9873a98044"
    35  )
    36  
    37  type platypusRegistry struct {
    38  	Address common.Address
    39  	Version int
    40  }
    41  
    42  type PlatypusCoin struct {
    43  	Symbol   string
    44  	Decimals uint8
    45  	Address  string
    46  	Name     string
    47  }
    48  
    49  type PlatypusPools struct {
    50  	pools     map[string]map[int]*PlatypusCoin
    51  	poolsLock sync.RWMutex
    52  }
    53  
    54  func (p *PlatypusPools) setPool(k string, v map[int]*PlatypusCoin) {
    55  	p.poolsLock.Lock()
    56  	defer p.poolsLock.Unlock()
    57  	p.pools[k] = v
    58  }
    59  func (p *PlatypusPools) getPool(k string) (map[int]*PlatypusCoin, bool) {
    60  	p.poolsLock.RLock()
    61  	defer p.poolsLock.RUnlock()
    62  	r, ok := p.pools[k]
    63  	return r, ok
    64  }
    65  
    66  func (p *PlatypusPools) poolsAddressNoLock() []string {
    67  	p.poolsLock.RLock()
    68  	defer p.poolsLock.RUnlock()
    69  	var values []string
    70  	for key := range p.pools {
    71  		values = append(values, key)
    72  	}
    73  	return values
    74  }
    75  
    76  // PlatypusScraper The scraper object for Platypus Finance
    77  type PlatypusScraper struct {
    78  	exchangeName string
    79  
    80  	// channels to signal events
    81  	run          bool
    82  	initDone     chan nothing
    83  	shutdown     chan nothing
    84  	shutdownDone chan nothing
    85  	resubscribe  chan string
    86  
    87  	errorLock sync.RWMutex
    88  	error     error
    89  	closed    bool
    90  
    91  	pairScrapers map[string]*PlatypusPairScraper
    92  	chanTrades   chan *dia.Trade
    93  
    94  	WsClient         *ethclient.Client
    95  	RestClient       *ethclient.Client
    96  	relDB            *models.RelDB
    97  	platypusCoins    map[string]*PlatypusCoin
    98  	pools            *PlatypusPools
    99  	screenPools      bool
   100  	basePoolRegistry platypusRegistry
   101  }
   102  
   103  // NewPlatypusScraper Returns a new exchange scraper
   104  func NewPlatypusScraper(exchange dia.Exchange, scrape bool, relDB *models.RelDB) *PlatypusScraper {
   105  
   106  	registries := []platypusRegistry{
   107  		{Version: 3, Address: common.HexToAddress(platypusMasterRegV3Addr)},
   108  		{Version: 2, Address: common.HexToAddress(platypusMasterRegV2Addr)},
   109  		{Version: 1, Address: common.HexToAddress(platypusMasterRegV1Addr)},
   110  	}
   111  
   112  	log.Infof("init rest and ws client for %s", exchange.BlockChain.Name)
   113  	restClient, err := ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", platypusRestDialEth))
   114  	if err != nil {
   115  		log.Fatal("init rest client: ", err)
   116  	}
   117  	wsClient, err := ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_WS", platypusWsDialEth))
   118  	if err != nil {
   119  		log.Fatal("init ws client: ", err)
   120  	}
   121  
   122  	// Only include pools with (minimum) liquidity bigger than given env var.
   123  	liquidityThreshold, err := strconv.ParseFloat(utils.Getenv("LIQUIDITY_THRESHOLD", "0"), 64)
   124  	if err != nil {
   125  		liquidityThreshold = float64(0)
   126  		log.Warnf("parse liquidity threshold:  %v. Set to default %v", err, liquidityThreshold)
   127  	}
   128  	// Only include pools with (minimum) liquidity USD value bigger than given env var.
   129  	liquidityThresholdUSD, err := strconv.ParseFloat(utils.Getenv("LIQUIDITY_THRESHOLD_USD", "0"), 64)
   130  	if err != nil {
   131  		liquidityThresholdUSD = float64(0)
   132  		log.Warnf("parse liquidity threshold:  %v. Set to default %v", err, liquidityThresholdUSD)
   133  	}
   134  
   135  	scraper := &PlatypusScraper{
   136  		exchangeName:  exchange.Name,
   137  		RestClient:    restClient,
   138  		WsClient:      wsClient,
   139  		initDone:      make(chan nothing),
   140  		shutdown:      make(chan nothing),
   141  		shutdownDone:  make(chan nothing),
   142  		pairScrapers:  make(map[string]*PlatypusPairScraper),
   143  		chanTrades:    make(chan *dia.Trade),
   144  		platypusCoins: make(map[string]*PlatypusCoin),
   145  		resubscribe:   make(chan string),
   146  		pools: &PlatypusPools{
   147  			pools: make(map[string]map[int]*PlatypusCoin),
   148  		},
   149  	}
   150  
   151  	scraper.relDB = relDB
   152  	// Load metadata from master registries
   153  	for _, registry := range registries {
   154  		err := scraper.loadPoolsAndCoins(registry, liquidityThreshold, liquidityThresholdUSD)
   155  		if err != nil {
   156  			log.Errorf("loadPoolsAndCoins error w %s registry (v%d): %s", registry.Address.Hex(), registry.Version, err)
   157  		}
   158  		log.Infof("metadata loaded, now scraper have %d pools data and %d coins", len(scraper.pools.pools), len(scraper.platypusCoins))
   159  	}
   160  
   161  	scraper.basePoolRegistry = platypusRegistry{Version: 3, Address: common.HexToAddress(platypusMasterRegV3Addr)}
   162  	scraper.screenPools = true
   163  
   164  	if scrape {
   165  		go scraper.mainLoop()
   166  	}
   167  	return scraper
   168  }
   169  
   170  // Close Closes any existing API connections, as well as channels of
   171  // pairScrapers from calls to ScrapePair
   172  func (s *PlatypusScraper) Close() error {
   173  	s.run = false
   174  	for _, pairScraper := range s.pairScrapers {
   175  		pairScraper.closed = true
   176  	}
   177  	s.WsClient.Close()
   178  	s.RestClient.Close()
   179  
   180  	close(s.shutdown)
   181  	<-s.shutdownDone
   182  	return nil
   183  }
   184  
   185  // ScrapePair returns a PairScraper that can be used to get trades for a single pair from the scraper
   186  func (s *PlatypusScraper) ScrapePair(pair dia.ExchangePair) (ps PairScraper, err error) {
   187  	return
   188  }
   189  
   190  // Returns the list of all available trade pairs
   191  func (s *PlatypusScraper) FetchAvailablePairs() (pairs []dia.ExchangePair, err error) {
   192  	return pairs, nil
   193  }
   194  
   195  // Channel returns a channel that can be used to receive trades
   196  func (s *PlatypusScraper) Channel() chan *dia.Trade {
   197  	return s.chanTrades
   198  }
   199  
   200  // FillSymbolData adds the name to the asset underlying @symbol on scraper
   201  func (s *PlatypusScraper) FillSymbolData(symbol string) (dia.Asset, error) {
   202  	return dia.Asset{Symbol: symbol}, nil
   203  }
   204  
   205  // NormalizePair accounts for the pair
   206  func (s *PlatypusScraper) NormalizePair(pair dia.ExchangePair) (dia.ExchangePair, error) {
   207  	return pair, nil
   208  }
   209  
   210  type PlatypusPairScraper struct {
   211  	parent *PlatypusScraper
   212  	pair   dia.ExchangePair
   213  	closed bool
   214  }
   215  
   216  // Close stops listening for trades of the pair associated with the scraper
   217  func (ps *PlatypusPairScraper) Close() error {
   218  	ps.parent.errorLock.RLock()
   219  	defer ps.parent.errorLock.RUnlock()
   220  	ps.closed = true
   221  	return nil
   222  }
   223  
   224  // Error returns an error when the channel Channel() is closed and nil otherwise
   225  func (ps *PlatypusPairScraper) Error() error {
   226  	s := ps.parent
   227  	s.errorLock.RLock()
   228  	defer s.errorLock.RUnlock()
   229  	return s.error
   230  }
   231  
   232  // Pair returns the pair this scraper is subscribed to
   233  func (ps *PlatypusPairScraper) Pair() dia.ExchangePair {
   234  	return ps.pair
   235  }
   236  
   237  // Load pools and coins metadata from master registry
   238  func (s *PlatypusScraper) loadPoolsAndCoins(registry platypusRegistry, liquidityThreshold float64, liquidityThresholdUSD float64) (err error) {
   239  	log.Infof("loading master contract %s version %d and querying registry", registry.Address.Hex(), registry.Version)
   240  	contractMaster, err := platypusfinance.NewBaseMasterPlatypusCaller(registry.Address, s.RestClient)
   241  	if err != nil {
   242  		log.Error("NewBaseMasterPlatypusCaller: ", err)
   243  		return err
   244  	}
   245  
   246  	poolCount, err := contractMaster.PoolLength(&bind.CallOpts{})
   247  	if err != nil {
   248  		log.Error("PoolLength: ", err)
   249  		return err
   250  	}
   251  
   252  	lowerBoundCount := 0
   253  	for i := 0; i < int(poolCount.Int64()); i++ {
   254  		asset, errPoolInfo := contractMaster.PoolInfo(&bind.CallOpts{}, big.NewInt(int64(i)))
   255  		if errPoolInfo != nil {
   256  			log.Error("PoolInfo: ", errPoolInfo)
   257  			return err
   258  		}
   259  		pool, errPool := s.relDB.GetPoolByAddress(dia.ETHEREUM, asset.LpToken.Hex())
   260  		if errPool != nil {
   261  			log.Errorf("Get pool %s by address: %v", asset.LpToken.Hex(), errPool)
   262  		}
   263  
   264  		lowLiqui := false
   265  		for _, av := range pool.Assetvolumes {
   266  			if av.Volume < liquidityThreshold {
   267  				log.Warnf("low liquidity on %s: %v", pool.Address, av.Volume)
   268  				lowLiqui = true
   269  				break
   270  			}
   271  		}
   272  		if lowLiqui {
   273  			continue
   274  		}
   275  
   276  		liquidity, lowerBound := pool.GetPoolLiquidityUSD()
   277  		// Discard pool if complete USD liquidity is below threshold.
   278  		if !lowerBound && liquidity < liquidityThresholdUSD {
   279  			continue
   280  		}
   281  		if lowerBound {
   282  			lowerBoundCount++
   283  		}
   284  
   285  		errPoolData := s.loadPoolData(asset.LpToken.Hex())
   286  		if errPoolData != nil {
   287  			log.Errorf("loadPoolData error at asset %s: %s", asset.LpToken.Hex(), errPoolData)
   288  			return errPoolData
   289  		}
   290  	}
   291  	log.Info("lowerBound: ", lowerBoundCount)
   292  
   293  	return err
   294  }
   295  
   296  // Runs in a goroutine until scraper is closed
   297  func (s *PlatypusScraper) mainLoop() {
   298  
   299  	s.run = true
   300  	for _, pool := range s.pools.poolsAddressNoLock() {
   301  		err := s.watchSwaps(pool)
   302  		if err != nil {
   303  			log.Error("watchSwaps: ", err)
   304  		}
   305  	}
   306  	if s.screenPools {
   307  		err := s.watchNewPools()
   308  		if err != nil {
   309  			log.Error("watchNewPools: ", err)
   310  		}
   311  	}
   312  
   313  	go func() {
   314  		defer func() {
   315  			log.Printf("Shutting down main work routine...\n")
   316  			if a := recover(); a != nil {
   317  				log.Errorf("work routine end. Recover msg: %+v", a)
   318  			}
   319  		}()
   320  
   321  		for s.run {
   322  			p := <-s.resubscribe
   323  			log.Info("resub to p: ", p)
   324  			if s.run {
   325  				if p == "NEW_POOLS" {
   326  					if s.screenPools {
   327  						log.Info("resubscribe to new pools")
   328  						err := s.watchNewPools()
   329  						if err != nil {
   330  							log.Error("watchNewPools in resubscribe: ", err)
   331  						}
   332  					}
   333  				} else {
   334  					log.Info("resubscribe to swaps from Pool: " + p)
   335  					err := s.watchSwaps(p)
   336  					if err != nil {
   337  						log.Error("watchSwaps in resubscribe: ", err)
   338  					}
   339  				}
   340  			}
   341  		}
   342  	}()
   343  
   344  	if s.run {
   345  		if len(s.pairScrapers) == 0 {
   346  			s.error = errors.New("no pairs to scrape provided")
   347  			log.Error(s.error.Error())
   348  		}
   349  	}
   350  
   351  	if s.error == nil {
   352  		s.error = errors.New("main loop terminated by Close()")
   353  	}
   354  	s.cleanup(nil)
   355  }
   356  
   357  func (s *PlatypusScraper) cleanup(err error) {
   358  	s.errorLock.Lock()
   359  	defer s.errorLock.Unlock()
   360  	if err != nil {
   361  		s.error = err
   362  	}
   363  	s.closed = true
   364  	close(s.shutdownDone)
   365  }
   366  
   367  func (s *PlatypusScraper) watchSwaps(pool string) error {
   368  	contractPool, err := platypusPoolABI.NewPoolFilterer(common.HexToAddress(pool), s.WsClient)
   369  	if err != nil {
   370  		log.Fatal(err)
   371  	}
   372  
   373  	sink := make(chan *platypusPoolABI.PoolSwap)
   374  	header, err := s.RestClient.HeaderByNumber(context.Background(), nil)
   375  	if err != nil {
   376  		log.Fatal(err)
   377  	}
   378  	startblock := header.Number.Uint64() - uint64(20)
   379  	log.Infof("subscribing for trades at %s pool", pool)
   380  	sub, err := contractPool.WatchSwap(&bind.WatchOpts{Start: &startblock}, sink, nil, nil)
   381  	if err != nil {
   382  		log.Error("contractPool.WatchSwap: ", err)
   383  		return err
   384  	}
   385  
   386  	go func() {
   387  		defer log.Printf("Shutting down pool work routine %s ...\n", pool)
   388  		defer sub.Unsubscribe()
   389  
   390  		subscribed := true
   391  		for s.run && subscribed {
   392  			select {
   393  			case err = <-sub.Err():
   394  				if err != nil {
   395  					log.Error("sub error: ", err)
   396  				}
   397  				subscribed = false
   398  				if s.run {
   399  					log.Warn("resubscribe pool: ", pool)
   400  					s.resubscribe <- pool
   401  					log.Info("scraper: ", s)
   402  				}
   403  				log.Warn("subscription error: ", err)
   404  			case swp := <-sink:
   405  				s.processSwap(pool, swp)
   406  			}
   407  		}
   408  	}()
   409  
   410  	return err
   411  }
   412  
   413  func (s *PlatypusScraper) processSwap(pool string, swap *platypusPoolABI.PoolSwap) {
   414  	foreignName, volume, price, baseToken, quoteToken, err := s.getSwapData(pool, swap)
   415  	if err != nil {
   416  		log.Error("getSwapDataPlatypus: ", err)
   417  	}
   418  	timestamp := time.Now().Unix()
   419  
   420  	trade := &dia.Trade{
   421  		Symbol:         quoteToken.Symbol,
   422  		Pair:           foreignName,
   423  		BaseToken:      baseToken,
   424  		QuoteToken:     quoteToken,
   425  		Price:          price,
   426  		Volume:         volume,
   427  		Time:           time.Unix(timestamp, 0),
   428  		ForeignTradeID: swap.Raw.TxHash.Hex() + "-" + fmt.Sprint(swap.Raw.Index),
   429  		PoolAddress:    pool,
   430  		Source:         s.exchangeName,
   431  		VerifiedPair:   true,
   432  	}
   433  
   434  	log.Infof("got trade in pool %s with tx %s", pool, trade.ForeignTradeID)
   435  	log.Info("trade: ", trade)
   436  	s.chanTrades <- trade
   437  }
   438  
   439  // getSwapDataPlatypus returns the foreign name, volume and price of a swap
   440  func (s *PlatypusScraper) getSwapData(pool string, swap *platypusPoolABI.PoolSwap) (foreignName string, volume float64, price float64, baseToken, quoteToken dia.Asset, err error) {
   441  	fromToken, ok := s.platypusCoins[swap.FromToken.Hex()]
   442  	if !ok {
   443  		log.Errorf("token not found: %s-%s", pool, swap.FromToken.Hex())
   444  		return
   445  	}
   446  	baseToken = dia.Asset{
   447  		Name:       fromToken.Name,
   448  		Address:    fromToken.Address,
   449  		Symbol:     fromToken.Symbol,
   450  		Blockchain: Exchanges[s.exchangeName].BlockChain.Name,
   451  	}
   452  
   453  	toToken, ok := s.platypusCoins[swap.ToToken.Hex()]
   454  	if !ok {
   455  		log.Errorf("token not found: %s-%s", pool, swap.ToToken.Hex())
   456  		return
   457  	}
   458  	quoteToken = dia.Asset{
   459  		Name:       toToken.Name,
   460  		Address:    toToken.Address,
   461  		Symbol:     toToken.Symbol,
   462  		Blockchain: Exchanges[s.exchangeName].BlockChain.Name,
   463  	}
   464  
   465  	// amountIn = AmountSold / math.Pow10( fromToken.Decimals )
   466  	amountIn, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(swap.FromAmount), new(big.Float).SetFloat64(math.Pow10(int(fromToken.Decimals)))).Float64()
   467  
   468  	// amountOut = AmountBought / math.Pow10( toToken.Decimals )
   469  	amountOut, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(swap.ToAmount), new(big.Float).SetFloat64(math.Pow10(int(toToken.Decimals)))).Float64()
   470  
   471  	volume = amountOut
   472  	price = amountIn / amountOut
   473  
   474  	foreignName = toToken.Symbol + "-" + fromToken.Symbol
   475  
   476  	return
   477  }
   478  
   479  func (s *PlatypusScraper) watchNewPools() error {
   480  	contractPool, err := platypusPoolABI.NewPoolFilterer(s.basePoolRegistry.Address, s.WsClient)
   481  	if err != nil {
   482  		log.Error("NewPoolFilterer: ", err)
   483  	}
   484  
   485  	sink := make(chan *platypusPoolABI.PoolAssetAdded)
   486  	header, err := s.RestClient.HeaderByNumber(context.Background(), nil)
   487  	if err != nil {
   488  		log.Fatal(err)
   489  	}
   490  	startblock := header.Number.Uint64() - uint64(platypusLookBackBlocks)
   491  	sub, err := contractPool.WatchAssetAdded(&bind.WatchOpts{Start: &startblock}, sink, nil, nil)
   492  	if err != nil {
   493  		log.Error("WatchPoolAdded: ", err)
   494  		return err
   495  	}
   496  
   497  	go func() {
   498  		log.Infof("subscribing to new asset added events at latest registry")
   499  		defer log.Info("Unsubscribed to new pools")
   500  		defer sub.Unsubscribe()
   501  		subscribed := true
   502  
   503  		for s.run && subscribed {
   504  
   505  			select {
   506  			case err = <-sub.Err():
   507  				if err != nil {
   508  					log.Error("subscription error in new pools: ", err)
   509  				}
   510  				subscribed = false
   511  				if s.run {
   512  					s.resubscribe <- "NEW_POOLS"
   513  				}
   514  			case vLog := <-sink:
   515  
   516  				if _, ok := s.pools.getPool(vLog.Asset.Hex()); !ok {
   517  					err = s.loadPoolData(vLog.Asset.Hex())
   518  					if err != nil {
   519  						log.Error("loadPoolData in new pools: ", err)
   520  					}
   521  					err = s.watchSwaps(vLog.Asset.Hex())
   522  					if err != nil {
   523  						log.Error("watchSwaps in new pools: ", err)
   524  					}
   525  				}
   526  			}
   527  		}
   528  	}()
   529  
   530  	return nil
   531  }
   532  
   533  func (s *PlatypusScraper) loadPoolData(asset string) error {
   534  	contractAsset, err := platypusAssetABI.NewAssetCaller(common.HexToAddress(asset), s.RestClient)
   535  	if err != nil {
   536  		log.Error("NewAssetCaller: ", err)
   537  		return err
   538  	}
   539  
   540  	pool, err := contractAsset.Pool(&bind.CallOpts{})
   541  	if err != nil {
   542  		log.Error("Pool: ", err)
   543  		return err
   544  	}
   545  
   546  	contractPool, err := platypusPoolABI.NewPoolCaller(pool, s.RestClient)
   547  	if err != nil {
   548  		log.Error("NewPoolCaller: ", err)
   549  		return err
   550  	}
   551  
   552  	poolTokenAddresses, errGetTokens := contractPool.GetTokenAddresses(&bind.CallOpts{})
   553  	if errGetTokens != nil {
   554  		symbol, err := contractAsset.Symbol(&bind.CallOpts{})
   555  		if err != nil {
   556  			log.Error("contractAsset.Symbol: ", err)
   557  		}
   558  		log.Warnf("error calling GetTokenAddresses for %s %s asset: %s", symbol, asset, errGetTokens)
   559  	}
   560  
   561  	var poolCoinsMap = make(map[int]*PlatypusCoin)
   562  	for cIdx, c := range poolTokenAddresses {
   563  		var symbol string
   564  		var decimals *big.Int
   565  		var name string
   566  		if c == common.HexToAddress("0x0000000000000000000000000000000000000000") {
   567  			continue
   568  		} else {
   569  			contractToken, err := platypusTokenABI.NewTokenCaller(c, s.RestClient)
   570  			if err != nil {
   571  				log.Error("NewTokenCaller: ", err)
   572  				continue
   573  			}
   574  
   575  			symbol, err = contractToken.Symbol(&bind.CallOpts{})
   576  			if err != nil {
   577  				log.Error("Symbol: ", err, c.Hex())
   578  				continue
   579  			}
   580  
   581  			decimals, err = contractToken.Decimals(&bind.CallOpts{})
   582  			if err != nil {
   583  				log.Error("Decimals: ", err)
   584  				continue
   585  			}
   586  
   587  			name, err = contractToken.Name(&bind.CallOpts{})
   588  			if err != nil {
   589  				log.Error("Name: ", err)
   590  				continue
   591  			}
   592  		}
   593  
   594  		poolCoinsMap[cIdx] = &PlatypusCoin{
   595  			Symbol:   symbol,
   596  			Decimals: uint8(decimals.Uint64()),
   597  			Name:     name,
   598  			Address:  c.Hex(),
   599  		}
   600  		s.platypusCoins[c.Hex()] = &PlatypusCoin{
   601  			Symbol:   symbol,
   602  			Decimals: uint8(decimals.Uint64()),
   603  			Name:     name,
   604  			Address:  c.Hex(),
   605  		}
   606  		s.pools.setPool(pool.Hex(), poolCoinsMap)
   607  	}
   608  
   609  	return nil
   610  }