github.com/status-im/status-go@v1.1.0/services/wallet/router/router_updates.go (about)

     1  package router
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/ethereum/go-ethereum/log"
     8  	"github.com/status-im/status-go/rpc/chain"
     9  	walletCommon "github.com/status-im/status-go/services/wallet/common"
    10  )
    11  
    12  var (
    13  	newBlockCheckIntervalMainnet  = 3 * time.Second
    14  	newBlockCheckIntervalOptimism = 1 * time.Second
    15  	newBlockCheckIntervalArbitrum = 200 * time.Millisecond
    16  
    17  	feeRecalculationTimeout = 5 * time.Minute
    18  )
    19  
    20  type fetchingLastBlock struct {
    21  	client    chain.ClientInterface
    22  	lastBlock uint64
    23  	closeCh   chan struct{}
    24  }
    25  
    26  func (r *Router) subscribeForUdates(chainID uint64) error {
    27  	if _, ok := r.clientsForUpdatesPerChains.Load(chainID); ok {
    28  		return nil
    29  	}
    30  
    31  	ethClient, err := r.rpcClient.EthClient(chainID)
    32  	if err != nil {
    33  		log.Error("Failed to get eth client", "error", err)
    34  		return err
    35  	}
    36  
    37  	flb := fetchingLastBlock{
    38  		client:    ethClient,
    39  		lastBlock: 0,
    40  		closeCh:   make(chan struct{}),
    41  	}
    42  	r.clientsForUpdatesPerChains.Store(chainID, flb)
    43  
    44  	r.startTimeoutForUpdates(flb.closeCh)
    45  
    46  	var ticker *time.Ticker
    47  	switch chainID {
    48  	case walletCommon.EthereumMainnet,
    49  		walletCommon.EthereumSepolia:
    50  		ticker = time.NewTicker(newBlockCheckIntervalMainnet)
    51  	case walletCommon.OptimismMainnet,
    52  		walletCommon.OptimismSepolia:
    53  		ticker = time.NewTicker(newBlockCheckIntervalOptimism)
    54  	case walletCommon.ArbitrumMainnet,
    55  		walletCommon.ArbitrumSepolia:
    56  		ticker = time.NewTicker(newBlockCheckIntervalArbitrum)
    57  	}
    58  
    59  	ctx, cancelCtx := context.WithCancel(context.Background())
    60  
    61  	go func() {
    62  		for {
    63  			select {
    64  			case <-ticker.C:
    65  				var blockNumber uint64
    66  				blockNumber, err := ethClient.BlockNumber(ctx)
    67  				if err != nil {
    68  					log.Error("Failed to get block number", "error", err)
    69  					continue
    70  				}
    71  
    72  				val, ok := r.clientsForUpdatesPerChains.Load(chainID)
    73  				if !ok {
    74  					log.Error("Failed to get fetchingLastBlock", "chain", chainID)
    75  					continue
    76  				}
    77  
    78  				flbLoaded, ok := val.(fetchingLastBlock)
    79  				if !ok {
    80  					log.Error("Failed to get fetchingLastBlock", "chain", chainID)
    81  					continue
    82  				}
    83  
    84  				if blockNumber > flbLoaded.lastBlock {
    85  					flbLoaded.lastBlock = blockNumber
    86  					r.clientsForUpdatesPerChains.Store(chainID, flbLoaded)
    87  
    88  					fees, err := r.feesManager.SuggestedFees(ctx, chainID)
    89  					if err != nil {
    90  						log.Error("Failed to get suggested fees", "error", err)
    91  						continue
    92  					}
    93  
    94  					r.lastInputParamsMutex.Lock()
    95  					uuid := r.lastInputParams.Uuid
    96  					r.lastInputParamsMutex.Unlock()
    97  
    98  					r.activeRoutesMutex.Lock()
    99  					if r.activeRoutes != nil && r.activeRoutes.Best != nil && len(r.activeRoutes.Best) > 0 {
   100  						for _, path := range r.activeRoutes.Best {
   101  							err = r.cacluateFees(ctx, path, fees, false, 0)
   102  							if err != nil {
   103  								log.Error("Failed to calculate fees", "error", err)
   104  								continue
   105  							}
   106  						}
   107  
   108  						_, err = r.checkBalancesForTheBestRoute(ctx, r.activeRoutes.Best)
   109  
   110  						sendRouterResult(uuid, r.activeRoutes, err)
   111  					}
   112  					r.activeRoutesMutex.Unlock()
   113  				}
   114  			case <-flb.closeCh:
   115  				ticker.Stop()
   116  				cancelCtx()
   117  				return
   118  			}
   119  		}
   120  	}()
   121  	return nil
   122  }
   123  
   124  func (r *Router) startTimeoutForUpdates(closeCh chan struct{}) {
   125  	dedlineTicker := time.NewTicker(feeRecalculationTimeout)
   126  	go func() {
   127  		for {
   128  			select {
   129  			case <-dedlineTicker.C:
   130  				r.unsubscribeFeesUpdateAccrossAllChains()
   131  				return
   132  			case <-closeCh:
   133  				dedlineTicker.Stop()
   134  				return
   135  			}
   136  		}
   137  	}()
   138  }
   139  
   140  func (r *Router) unsubscribeFeesUpdateAccrossAllChains() {
   141  	r.clientsForUpdatesPerChains.Range(func(key, value interface{}) bool {
   142  		flb, ok := value.(fetchingLastBlock)
   143  		if !ok {
   144  			log.Error("Failed to get fetchingLastBlock", "chain", key)
   145  			return false
   146  		}
   147  
   148  		close(flb.closeCh)
   149  		r.clientsForUpdatesPerChains.Delete(key)
   150  		return true
   151  	})
   152  }