public Task <ApiKey> GenerateApiKeyAsync(string clientId, string walletId, bool apiv2Only = false, string walletName = null) { var id = Guid.NewGuid(); var token = GenerateJwtToken(id.ToString(), clientId, walletId, apiv2Only, walletName); var key = new ApiKey { Id = id, Token = token, ClientId = clientId, WalletId = walletId, Created = DateTime.UtcNow, Apiv2Only = apiv2Only }; _cqrsEngine.SendCommand( new CreateApiKeyCommand { ApiKey = key.Id.ToString(), Token = token, ClientId = clientId, WalletId = walletId, Created = DateTime.UtcNow, Apiv2Only = apiv2Only }, "api-key", "api-key"); return(Task.FromResult(key)); }
private async Task ProcessExternalCashin(CashInOutQueueMessage message) { var asset = await _assetsServiceWithCache.TryGetAssetAsync(message.AssetId); if (asset.Blockchain != Blockchain.Bitcoin || asset.IsTrusted && asset.Id != LykkeConstants.BitcoinAssetId) { return; } if (asset.Id == LykkeConstants.BitcoinAssetId) { var cashinType = await _bitcoinCashinTypeRepository.GetAsync(message.Id); if (cashinType == null || !cashinType.IsSegwit) { _cqrsEngine.SendCommand(new CreateOffchainCashoutRequestCommand { Id = message.Id, ClientId = message.ClientId, AssetId = message.AssetId, Amount = (decimal)message.Amount.ParseAnyDouble() }, BoundedContexts.TxHandler, BoundedContexts.Offchain); } else { _cqrsEngine.SendCommand(new SegwitTransferCommand { Id = message.Id, Address = cashinType.Address }, BoundedContexts.TxHandler, BoundedContexts.Bitcoin); } } }
public Task <IAssetPair> AddAsync(IAssetPair assetPair) { _cqrsEngine.SendCommand( new CreateAssetPairCommand { AssetPair = Mapper.Map <AssetPair>(assetPair) }, BoundedContext.Name, BoundedContext.Name); return(Task.FromResult(assetPair)); }
public async Task <IAsset> AddAsync(IAsset asset) { await ValidateAsset(asset); _cqrsEngine.SendCommand( new CreateAssetCommand { Asset = Mapper.Map <Asset>(asset) }, BoundedContext.Name, BoundedContext.Name); return(asset); }
public async Task <IAssetPair> AddAsync(IAssetPair assetPair) { await _assetPairRepository.UpsertAsync(assetPair); await _myNoSqlWriter.TryInsertOrReplaceAsync(AssetPairNoSql.Create(assetPair)); //todo: remove cqrs _cqrsEngine.SendCommand( new CreateAssetPairCommand { AssetPair = Mapper.Map <AssetPair>(assetPair) }, BoundedContext.Name, BoundedContext.Name); return(assetPair); }
private async Task Execute(ITimerTrigger timer, TimerTriggeredHandlerArgs args, CancellationToken cancellationToken) { var checkDate = await _lastMomentRepo.GetLastEventMomentAsync(_settings.AssetId) ?? _startedAt; // run heartbeat with delay if there are no cashout registered if (DateTime.UtcNow - checkDate > _settings.MaxCashoutInactivePeriod && !await _cashoutLockRepository.IsLockedAsync(_settings.AssetId)) { var opId = Guid.NewGuid(); _log.Info("Starting heartbeat cashout", context: new { opId, checkDate, _settings.AssetId }); _cqrsEngine.SendCommand(new StartHeartbeatCashoutCommand { Amount = _settings.Amount, AssetId = _settings.AssetId, OperationId = opId, ToAddress = _settings.ToAddress, ToAddressExtension = _settings.ToAddressExtension, MaxCashoutInactivePeriod = _settings.MaxCashoutInactivePeriod, ClientId = _settings.ClientId, FeeCashoutTargetClientId = _settings.FeeCashoutTargetClientId, ClientBalance = _settings.ClientBalance }, HeartbeatCashoutBoundedContext.Name, HeartbeatCashoutBoundedContext.Name); } }
public async Task <string> AssignContract(string userAddress) { var contractAddress = await GetContractAddress(userAddress); if (string.IsNullOrEmpty(contractAddress)) { var pool = _poolFactory.Get(Constants.Erc20DepositContractPoolQueue); contractAddress = await pool.GetContractAddress(); var command = new AssignErc223DepositToUserCommand() { UserAddress = userAddress, ContractAddress = contractAddress }; try { _cqrsEngine.SendCommand(command, "blockchain.ethereum.core.api", EthereumBoundedContext.Name); } catch (Exception e) { await pool.PushContractAddress(contractAddress); throw; } } return(contractAddress); }
public async Task <IActionResult> AddOrEditStaff(AddStaffDialogViewModel vm) { if (string.IsNullOrEmpty(vm.FirstName)) { return(this.JsonFailResult(Phrases.FieldShouldNotBeEmpty, ErrorMessageAnchor)); } if (string.IsNullOrEmpty(vm.LastName)) { return(this.JsonFailResult(Phrases.FieldShouldNotBeEmpty, ErrorMessageAnchor)); } if (!vm.Email?.IsValidEmail() ?? vm.IsNewStaff) { return(this.JsonFailResult(Phrases.FieldShouldNotBeEmpty, ErrorMessageAnchor)); } if (vm.IsNewStaff && string.IsNullOrEmpty(vm.Password)) { return(this.JsonFailResult(Phrases.FieldShouldNotBeEmpty, ErrorMessageAnchor)); } if (vm.IsNewStaff) { _cqrsEngine.SendCommand( _mapper.Map <RegisterEmployeeCommand>(vm), "lykkepay-employee-registration-ui", "lykkepay-employee-registration"); } else { EmployeeModel employee = await _payInvoiceClient.GetEmployeeAsync(vm.Id); var updateEmployeeCommand = _mapper.Map <UpdateEmployeeCommand>(vm); updateEmployeeCommand.Email = employee.Email; _cqrsEngine.SendCommand( updateEmployeeCommand, "lykkepay-employee-registration-ui", "lykkepay-employee-registration"); } return(this.JsonRequestResult("#staffList", Url.Action("StaffsList"), new StaffsPageViewModel { SelectedMerchant = vm.SelectedMerchant })); }
public async Task <IAsset> AddAsync(IAsset asset) { await ValidateAsset(asset); await _assetRepository.InsertOrReplaceAsync(asset); await _myNoSqlWriter.TryInsertOrReplaceAsync(AssetNoSql.Create(asset)); //todo: remove cqrs _cqrsEngine.SendCommand( new CreateAssetCommand { Asset = Mapper.Map <Asset>(asset) }, BoundedContext.Name, BoundedContext.Name); return(asset); }
private Task <bool> SendEventToCqrs(CoinEvent @event) { _cqrsEngine.SendCommand(new ProcessEthCoinEventCommand { Additional = @event.Additional, Amount = @event.Amount, CoinEventType = @event.CoinEventType, ContractAddress = @event.ContractAddress, EventTime = @event.EventTime, FromAddress = @event.FromAddress, OperationId = @event.OperationId, ToAddress = @event.ToAddress, TransactionHash = @event.TransactionHash, }, BoundedContexts.EthereumCommands, BoundedContexts.EthereumCommands); return(Task.FromResult(true)); }
public async Task <IActionResult> TwilioCallback([FromForm] TwilioCallbackModel model) { if (model == null) { return(Ok()); } _log.WriteInfo(nameof(TwilioCallback), model.Sanitize(), "Twilio callback"); if (!string.IsNullOrEmpty(model.MessageSid)) { var sms = await _smsRepository.GetByMessageIdAsync(model.MessageSid); if (sms == null) { _log.WriteInfo(nameof(TwilioCallback), model.MessageSid, $"Sms message with messageId = {model.MessageSid} not found"); return(Ok()); } switch (model.MessageStatus) { case TwilioMessageStatus.Delivered: _cqrsEngine.SendCommand(new SmsDeliveredCommand { Message = sms }, "sms", "sms"); break; case TwilioMessageStatus.Failed: case TwilioMessageStatus.Undelivered: _cqrsEngine.SendCommand(new SmsNotDeliveredCommand { Message = sms, Error = $"status = {model.MessageStatus}" }, "sms", "sms"); break; default: _log.WriteWarning(nameof(TwilioCallback), model.MessageSid, $"status = {model.MessageStatus}, callback processing is skipped"); break; } } return(Ok()); }
private Task ProcessMessage(CashTransferEvent transfer) { var cashTransfer = ToOldModel(transfer); _cqrsEngine.SendCommand( new Commands.SaveTransferOperationStateCommand { QueueMessage = cashTransfer }, BoundedContexts.TxHandler, BoundedContexts.Operations); return(Task.CompletedTask); }
public IActionResult ConfirmOperation([FromBody] OperationConfirmationModel model) { var command = new ConfirmCommand { OperationId = model.OperationId, ClientId = Guid.Parse(_requestContext.ClientId), Confirmation = model.Signature.Code }; _cqrsEngine.SendCommand(command, "apiv2", OperationsBoundedContext.Name); return(Ok()); }
private void UpdateTotalBalances(Lykke.MatchingEngine.Connector.Models.Events.Common.Header header, List <Lykke.MatchingEngine.Connector.Models.Events.Common.BalanceUpdate> updates) { foreach (var wallet in updates) { _cqrsEngine.SendCommand(new UpdateTotalBalanceCommand { AssetId = wallet.AssetId, BalanceDelta = ParseNullabe(wallet.NewBalance) - ParseNullabe(wallet.OldBalance), SequenceNumber = header.SequenceNumber }, "balances", "balances"); } }
private void ProcessBalance( WalletBalance depositWallet, IReadOnlyDictionary <string, EnrolledBalance> enrolledBalances, int batchSize) { if (!_assets.TryGetValue(depositWallet.AssetId, out var asset)) { if (!_warningAssets.Contains(depositWallet.AssetId)) { _log.Warning(nameof(ProcessBalance), "Lykke asset for the blockchain asset is not found", context: depositWallet); _warningAssets.Add(depositWallet.AssetId); } return; } enrolledBalances.TryGetValue(GetEnrolledBalancesDictionaryKey(depositWallet.Address, depositWallet.AssetId), out var enrolledBalance); var cashinCouldBeStarted = CashinAggregate.CouldBeStarted( depositWallet.Balance, depositWallet.Block, enrolledBalance?.Balance ?? 0, enrolledBalance?.Block ?? 0, asset.Accuracy); if (!cashinCouldBeStarted) { return; } _cqrsEngine.SendCommand ( new LockDepositWalletCommand { BlockchainType = _blockchainType, BlockchainAssetId = depositWallet.AssetId, DepositWalletAddress = depositWallet.Address, DepositWalletBalance = depositWallet.Balance, DepositWalletBlock = depositWallet.Block, AssetId = asset.Id, AssetAccuracy = asset.Accuracy, BlockchainAssetAccuracy = GetAssetAccuracy(asset.BlockchainIntegrationLayerAssetId, batchSize), CashinMinimalAmount = (decimal)asset.CashinMinimalAmount, HotWalletAddress = _hotWalletAddress }, BlockchainCashinDetectorBoundedContext.Name, BlockchainCashinDetectorBoundedContext.Name ); }
public void StartClaimTransaction() { var transactionId = Guid.NewGuid(); _log.Info($"Starting claim transaction {transactionId}", context: _starterSettings); _cqrsEngine.SendCommand(new StartTransactionCommand { TransactionId = transactionId, Address = _starterSettings.NeoHotWalletAddress, GasAssetId = _starterSettings.GasAssetId, NeoAssetId = _starterSettings.NeoAssetId }, NeoClaimTransactionsExecutorBoundedContext.Name, NeoClaimTransactionsExecutorBoundedContext.Name); }
private Task ProcessMessage(ExecutionEvent evt) { _log.Info("Processing execution event", evt); foreach (var order in evt.Orders) { switch (order.OrderType) { case OrderType.Market: var marketOrder = ToOldMarketOrder(order); _cqrsEngine.SendCommand( new Commands.CreateTradeCommand { QueueMessage = marketOrder }, BoundedContexts.TxHandler, BoundedContexts.Trades); break; case OrderType.Limit: case OrderType.StopLimit: var limitOrder = ToOldLimitOrder(order); _cqrsEngine.SendCommand( new ProcessLimitOrderCommand { LimitOrder = limitOrder }, BoundedContexts.TxHandler, BoundedContexts.TxHandler); break; default: throw new NotSupportedException($"Order type {order.OrderType} is not supported"); } } return(Task.CompletedTask); }
private async Task HandleDetectedTransaction(string address, IBlockchainTransaction tx, IBalanceChangeTransaction balanceChangeTx) { var internalOperation = await _internalOperationsRepository.GetAsync(tx.Hash); if (internalOperation?.CommandType == BitCoinCommands.Transfer || IsExternalCashIn(address, tx, internalOperation) || IsOtherClientsCashOut(address, tx, internalOperation)) { var processTransactionCommand = new ProcessTransactionCommand { TransactionHash = balanceChangeTx.Hash }; _cqrsEngine.SendCommand(processTransactionCommand, "transactions", "transactions"); } }
public IActionResult RequestClientHistoryCsv([FromBody] RequestClientHistoryCsvRequestModel model) { var id = Guid.NewGuid().ToString(); _cqrsEngine.SendCommand(new ExportClientHistoryCommand { Id = id, ClientId = _requestContext.ClientId, OperationTypes = model.OperationType, AssetId = model.AssetId, AssetPairId = model.AssetPairId }, null, HistoryExportBuilderBoundedContext.Name); return(Ok(new RequestClientHistoryCsvResponseModel { Id = id })); }
public async Task <ActionResult> Webhook() { _log.Info("Webhook"); try { WebhookEvent webhookEvent; using (var reader = new StreamReader(Request.Body)) { var body = await reader.ReadToEndAsync(); var data = _encryptionService.Decrypt(body); webhookEvent = JsonConvert.DeserializeObject <WebhookEvent>(data); if (webhookEvent?.OriginalTxnStatus == TransactionStatus.Pending) { _log.Info("Skip webhook event with pending status"); return(Ok()); } } if (webhookEvent != null) { string data = webhookEvent.ToJson(); _log.Info($"Webhook data {webhookEvent.TxnReference}", context: data); await _rawLogRepository.RegisterEventAsync(RawLogEvent.Create(nameof(Webhook), data)); _cqrsEngine.SendCommand(new CashInCommand { TransactionId = webhookEvent.TxnReference, Request = data }, Link4PayBoundedContext.Name, Link4PayBoundedContext.Name); } } catch (Exception ex) { _log.Error(ex); return(Ok()); } return(Ok()); }
public async Task <IActionResult> ExecutePlanAsync( Guid planId) { if (await _distributionPlanService.PlanExistsAsync(planId)) { _cqrsEngine.SendCommand ( command: new ExecuteDistributionPlanCommand { PlanId = planId }, boundedContext: NeoGasDistributionPlannerBoundedContext.Name, remoteBoundedContext: NeoGasDistributionPlannerBoundedContext.Name ); return(Accepted()); } else { return(NotFound($"Distribution plan [{planId}] has not been found.")); } }
public IActionResult SendSms([FromBody] SmsModel model) { if (model == null) { return(BadRequest("Model is null")); } if (!ModelState.IsValid) { return(BadRequest(ModelState.GetError())); } var phone = model.Phone.GetValidPhone(); if (phone == null) { ModelState.AddModelError(nameof(model.Phone), "invalid phone number"); return(BadRequest(ModelState.GetError())); } if (model.Message.Length > 160) { ModelState.AddModelError(nameof(model.Message), "Message length is too long (max. 160 chars)"); return(BadRequest(ModelState.GetError())); } try { _cqrsEngine.SendCommand(new ProcessSmsCommand { Message = model.Message, Phone = model.Phone }, "sms", "sms"); return(Ok()); } catch (Exception ex) { _log.WriteError(nameof(SendSms), new { Phone = model.Phone.SanitizePhone() }, ex); return(BadRequest(ErrorResponse.Create("Technical problems"))); } }
private async Task ProcessChunkAsync(IEnumerable <IForwardWithdrawal> items) { foreach (var forwardWithdrawal in items) { try { var daysToTrigger = (await _assetsServiceWithCache.TryGetAssetAsync(forwardWithdrawal.AssetId))?.ForwardFrozenDays; if (!daysToTrigger.HasValue) { throw new InvalidOperationException(string.Format(DaysToTriggerCouldNotBeResolvedErrorMessage, forwardWithdrawal.AssetId)); } if (forwardWithdrawal.IsDue(TimeSpan.FromDays(daysToTrigger.Value))) { if (forwardWithdrawal.DateTimeTimestampDifferenceTooBig(_criticalSpan)) { throw new InvalidOperationException(DifferenceTooBigErrorMessage); } _cqrsEngine.SendCommand( new RemoveEntryCommand { ClientId = forwardWithdrawal.ClientId, Id = forwardWithdrawal.Id }, BoundedContext.ForwardWithdrawal, BoundedContext.ForwardWithdrawal); } } catch (Exception e) { _log.Error(e, forwardWithdrawal.ToJson()); } } }
private async Task Execute(ITimerTrigger timer, TimerTriggeredHandlerArgs args, CancellationToken cancellationToken) { var lockModel = await _cashoutLockRepository.GetLockAsync(_settings.AssetId); if (lockModel != null && DateTime.UtcNow - lockModel.Value.lockedAt > _settings.ExecutionTimeout) { _log.Warning("Cashout finished after timeout", context: new { lockModel.Value.operationId, lockModel.Value.lockedAt, _settings.ExecutionTimeout }); _cqrsEngine.SendCommand(new ReleaseCashoutLockCommand { OperationId = lockModel.Value.operationId, AssetId = _settings.AssetId }, HeartbeatCashoutBoundedContext.Name, HeartbeatCashoutBoundedContext.Name); } }
public async Task <IActionResult> Cashout([FromBody] CreateCashoutRequest cmd, [FromQuery] Guid?id) { if (string.IsNullOrWhiteSpace(cmd.DestinationAddress) || string.IsNullOrWhiteSpace(cmd.AssetId) || cmd.Volume == 0m) { throw LykkeApiErrorException.BadRequest(LykkeApiErrorCodes.Service.InvalidInput); } var asset = await _assetsServiceWithCache.TryGetAssetAsync(cmd.AssetId); if (asset == null) { return(NotFound($"Asset '{cmd.AssetId}' not found.")); } var balance = await _balancesClient.GetClientBalanceByAssetId(new ClientBalanceByAssetIdModel(cmd.AssetId, _requestContext.ClientId)); var cashoutSettings = await _clientAccountClient.ClientSettings.GetCashOutBlockSettingsAsync(_requestContext.ClientId); var kycStatus = await _kycStatusService.GetKycStatusAsync(_requestContext.ClientId); if (_baseSettings.EnableTwoFactor) { try { if ((await _confirmationCodesClient.Google2FaIsClientBlacklistedAsync(_requestContext.ClientId)).IsClientBlacklisted) { throw LykkeApiErrorException.Forbidden(LykkeApiErrorCodes.Service.SecondFactorCheckForbiden); } } catch (ApiException e) { if (e.StatusCode == HttpStatusCode.BadRequest) { throw LykkeApiErrorException.Forbidden(LykkeApiErrorCodes.Service.TwoFactorRequired); } } } var operationId = id ?? Guid.NewGuid(); var cashoutCommand = new CreateCashoutCommand { OperationId = operationId, DestinationAddress = cmd.DestinationAddress, DestinationAddressExtension = cmd.DestinationAddressExtension, Volume = cmd.Volume, Asset = new AssetCashoutModel { Id = asset.Id, DisplayId = asset.DisplayId, MultiplierPower = asset.MultiplierPower, AssetAddress = asset.AssetAddress, Accuracy = asset.Accuracy, BlockchainIntegrationLayerId = asset.BlockchainIntegrationLayerId, Blockchain = asset.Blockchain.ToString(), Type = asset.Type?.ToString(), IsTradable = asset.IsTradable, IsTrusted = asset.IsTrusted, KycNeeded = asset.KycNeeded, BlockchainWithdrawal = asset.BlockchainWithdrawal, CashoutMinimalAmount = (decimal)asset.CashoutMinimalAmount, LowVolumeAmount = (decimal?)asset.LowVolumeAmount ?? 0, LykkeEntityId = asset.LykkeEntityId }, Client = new ClientCashoutModel { Id = new Guid(_requestContext.ClientId), Balance = balance?.Balance ?? 0, CashOutBlocked = cashoutSettings.CashOutBlocked, KycStatus = kycStatus.ToString(), ConfirmationType = "google" }, GlobalSettings = new GlobalSettingsCashoutModel { MaxConfirmationAttempts = _baseSettings.MaxTwoFactorConfirmationAttempts, TwoFactorEnabled = _baseSettings.EnableTwoFactor, CashOutBlocked = false, // TODO FeeSettings = new FeeSettingsCashoutModel { TargetClients = new Dictionary <string, string> { { "Cashout", _feeSettings.TargetClientId.Cashout } } } } }; _cqrsEngine.SendCommand(cashoutCommand, "apiv2", OperationsBoundedContext.Name); return(Created(Url.Action("Get", new { operationId }), operationId)); }
public async void SendWaitForTransactionEndingCommand([FromBody] EnrollToMatchingEngineCommand command) { _cqrsEngine.SendCommand(command, $"{CqrsModule.Self}.saga", CqrsModule.Self); }
public async Task SendWaitForTransactionEndingCommand([FromBody] WaitForTransactionEndingCommand command) { _cqrsEngine.SendCommand(command, $"{CqrsModule.TransactionExecutor}.saga", CqrsModule.TransactionExecutor); }
public async Task <Guid> CreateWithdrawalAsync( string requestId, string clientId, string walletId, string assetId, decimal volume, string destinationAddress, string destinationAddressExtension) { var uniqueRequestId = $"{walletId}_{requestId}"; var validationResult = await _validationService.ValidateWithdrawalRequestAsync(assetId, volume); if (validationResult != null) { throw HftApiException.Create(validationResult.Code, validationResult.Message).AddField(validationResult.FieldName); } var operationId = Guid.NewGuid(); var payload = await _idempotencyService.CreateEntityOrGetPayload(uniqueRequestId, operationId.ToString()); if (payload != null) { operationId = Guid.Parse(payload); } var asset = await _assetsService.GetAssetByIdAsync(assetId); if (asset.BlockchainIntegrationType != BlockchainIntegrationType.Sirius) { throw HftApiException.Create(HftApiErrorCode.ActionForbidden, "Asset unavailable"); } var balances = await _balanceService.GetBalancesAsync(walletId); var cashoutSettings = await _clientAccountClient.ClientSettings.GetCashOutBlockSettingsAsync(clientId); var kycStatus = await _kycStatusService.GetKycStatusAsync(clientId); var cashoutCommand = new CreateCashoutCommand { OperationId = operationId, WalletId = walletId, DestinationAddress = destinationAddress, DestinationAddressExtension = destinationAddressExtension, Volume = volume, Asset = new AssetCashoutModel { Id = asset.AssetId, DisplayId = asset.Symbol, MultiplierPower = asset.MultiplierPower, AssetAddress = asset.AssetAddress, Accuracy = asset.Accuracy, BlockchainIntegrationLayerId = asset.BlockchainIntegrationLayerId, Blockchain = asset.Blockchain.ToString(), Type = asset.Type?.ToString(), IsTradable = asset.IsTradable, IsTrusted = asset.IsTrusted, KycNeeded = asset.KycNeeded, BlockchainWithdrawal = asset.BlockchainWithdrawal, CashoutMinimalAmount = (decimal)asset.CashoutMinimalAmount, LowVolumeAmount = (decimal?)asset.LowVolumeAmount ?? 0, LykkeEntityId = asset.LykkeEntityId, SiriusAssetId = asset.SiriusAssetId, BlockchainIntegrationType = Lykke.Service.Assets.Client.Models.BlockchainIntegrationType.Sirius }, Client = new ClientCashoutModel { Id = new Guid(clientId), Balance = balances.SingleOrDefault(x => x.AssetId == assetId)?.Available ?? 0, CashOutBlocked = cashoutSettings.CashOutBlocked, KycStatus = kycStatus.ToString() }, GlobalSettings = new GlobalSettingsCashoutModel { MaxConfirmationAttempts = -1, TwoFactorEnabled = false, CashOutBlocked = false, // TODO FeeSettings = new FeeSettingsCashoutModel { TargetClients = new Dictionary <string, string> { { "Cashout", _feeSettings.WithdrawalFeeDestinationClientId } } } } }; _cqrsEngine.SendCommand(cashoutCommand, "hft-api", OperationsBoundedContext.Name); return(operationId); }