public async Task FetchAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Fetching...");

            while (!cancellationToken.IsCancellationRequested)
            {
                var startDate = DateTime.UtcNow;

                var exchanges = await _exchangeRepository.GetAllAsync();

                while (!cancellationToken.IsCancellationRequested)
                {
                    _logger.LogInformation("Processing cycle of {count} exchanges started", exchanges.Count());

                    var exchangeProcessingStartTime = DateTime.UtcNow;

                    foreach (var exchange in exchanges)
                    {
                        var fromBlock =
                            await _startBlockService.GetAsync(exchange.Id, exchange.BlockNumber);

                        var recentBlock = await _recentBlockProvider.GetAsync() - _fetcherSettings.RecentBlockReachLimit;

                        var fetcher = _exchangeEventsFetcherFactory(exchange.Id);

                        _logger.LogDebug("Fetching events for exchange {exchangeAddress}...", exchange.Id);

                        while (fromBlock <= recentBlock)
                        {
                            var toBlock = Math.Min(fromBlock + _fetcherSettings.BlocksPerIteration, recentBlock);

                            _logger.LogDebug("Fetching events from {fromBlock} to {toBlock}...", fromBlock, toBlock);

                            var events = await fetcher.FetchAsync(fromBlock, toBlock);

                            _logger.LogDebug("{amount} event(s) fetched", events.Count);

                            await _exchangeEventsProcessor.ProcessAsync(events);

                            await _startBlockService.UpdateAsync(exchange.Id, toBlock);

                            fromBlock = toBlock + 1;
                        }
                    }

                    var elapsedMs = (int)(DateTime.UtcNow - exchangeProcessingStartTime).TotalMilliseconds;
                    if (elapsedMs < _fetcherSettings.DelayMs)
                    {
                        await Task.Delay(_fetcherSettings.DelayMs - elapsedMs, cancellationToken);
                    }

                    if (DateTime.UtcNow - startDate > TimeSpan.FromMilliseconds(_fetcherSettings.UpdateExchangesIntervalMs))
                    {
                        break;
                    }
                }
            }
        }
        private async Task <(ulong FromBlock, ulong ToBlock, bool IsFull)> GetRange(ulong startBlock)
        {
            var recentBlock = await _recentBlockProvider.GetAsync();

            var fromBlock = Math.Min(startBlock, recentBlock - _settings.RecentBlockReachLimit);
            var toBlock   = Math.Min(startBlock + _settings.BlocksPerIteration,
                                     recentBlock - _settings.RecentBlockReachLimit);

            return(fromBlock, toBlock, toBlock - fromBlock == _settings.BlocksPerIteration);
        }