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 }