public async Task <LimitationCheckResult> CheckCashOperationLimitAsync( string clientId, string assetId, double amount, CurrencyOperationType currencyOperationType) { var originalAsset = assetId; var originalAmount = amount; amount = Math.Abs(amount); if (currencyOperationType == CurrencyOperationType.CryptoCashOut) { var asset = _assets.Get(assetId); if (amount < asset.CashoutMinimalAmount) { var minimalAmount = asset.CashoutMinimalAmount.GetFixedAsString(asset.Accuracy).TrimEnd('0'); return(new LimitationCheckResult { IsValid = false, FailMessage = $"The minimum amount to cash out is {minimalAmount}" }); } if (asset.LowVolumeAmount.HasValue && amount < asset.LowVolumeAmount) { var settings = await _limitSettingsRepository.GetAsync(); var timeout = TimeSpan.FromMinutes(settings.LowCashOutTimeoutMins); var callHistory = await _callTimeLimitsRepository.GetCallHistoryAsync("CashOutOperation", clientId, timeout); var cashoutEnabled = !callHistory.Any() || callHistory.IsCallEnabled(timeout, settings.LowCashOutLimit); if (!cashoutEnabled) { return new LimitationCheckResult { IsValid = false, FailMessage = "You have exceeded cash out operations limit. Please try again later." } } ; } } if (currencyOperationType == CurrencyOperationType.SwiftTransferOut) { var error = await CheckSwiftWithdrawLimitations(assetId, (decimal)amount); if (error != null) { return(new LimitationCheckResult { IsValid = false, FailMessage = error }); } } if (currencyOperationType != CurrencyOperationType.CryptoCashIn && currencyOperationType != CurrencyOperationType.CryptoCashOut) { (assetId, amount) = await _currencyConverter.ConvertAsync( assetId, _currencyConverter.DefaultAsset, amount); } var limitationTypes = LimitMapHelper.MapOperationType(currencyOperationType); var typeLimits = _limits.Where(l => limitationTypes.Contains(l.LimitationType)).ToList(); if (!typeLimits.Any()) { try { await _limitOperationsApi.AddOperationAttemptAsync( clientId, originalAsset, originalAmount, currencyOperationType == CurrencyOperationType.CardCashIn ?CashOperationsTimeoutInMinutes : _attemptRetainInMinutes, currencyOperationType); } catch (Exception ex) { _log.Error(ex, context: new { Type = "Attempt", clientId, originalAmount, originalAsset }); } return(new LimitationCheckResult { IsValid = true }); } //To handle parallel request await _lock.WaitAsync(); try { var assetLimits = typeLimits.Where(l => l.Asset == assetId).ToList(); string error = await DoPeriodCheckAsync( assetLimits, LimitationPeriod.Month, clientId, assetId, amount, currencyOperationType, false); if (error != null) { return new LimitationCheckResult { IsValid = false, FailMessage = error } } ; error = await DoPeriodCheckAsync( assetLimits, LimitationPeriod.Day, clientId, assetId, amount, currencyOperationType, false); if (error != null) { return new LimitationCheckResult { IsValid = false, FailMessage = error } } ; if (currencyOperationType == CurrencyOperationType.CryptoCashOut) { assetLimits = typeLimits.Where(l => l.Asset == _currencyConverter.DefaultAsset).ToList(); var(assetTo, convertedAmount) = await _currencyConverter.ConvertAsync( assetId, _currencyConverter.DefaultAsset, amount, true); error = await DoPeriodCheckAsync( assetLimits, LimitationPeriod.Month, clientId, assetTo, convertedAmount, currencyOperationType, true); if (error != null) { return new LimitationCheckResult { IsValid = false, FailMessage = error } } ; error = await DoPeriodCheckAsync( assetLimits, LimitationPeriod.Day, clientId, assetTo, convertedAmount, currencyOperationType, true); if (error != null) { return new LimitationCheckResult { IsValid = false, FailMessage = error } } ; } try { await _limitOperationsApi.AddOperationAttemptAsync( clientId, originalAsset, originalAmount, currencyOperationType == CurrencyOperationType.CardCashIn ?CashOperationsTimeoutInMinutes : _attemptRetainInMinutes, currencyOperationType); } catch (Exception ex) { _log.Error(ex, context: new { Type = "Attempt", clientId, originalAmount, originalAsset }); } } finally { _lock.Release(); } return(new LimitationCheckResult { IsValid = true }); }