public async Task <CustomerProfileErrorCodes> RequestCustomerProfileDeactivation(string customerId) { return(await _transactionRunner.RunWithTransactionAsync(async txContext => { var customerProfile = await _customerProfileRepository.GetByCustomerIdAsync(customerId, includeNotVerified: true, includeNotActive: true, txContext); if (customerProfile == null) { return CustomerProfileErrorCodes.CustomerProfileDoesNotExist; } if (customerProfile.Status != CustomerProfileStatus.Active) { return CustomerProfileErrorCodes.CustomerIsNotActive; } await _customerProfileRepository.ChangeProfileStatus(customerId, CustomerProfileStatus.PendingDeactivation, txContext); await _deactivationRequestedPublisher.PublishAsync(new CustomerProfileDeactivationRequestedEvent { CustomerId = customerId }); return CustomerProfileErrorCodes.None; })); }
private async Task <string> BuildTransactionAsync( string operationId, string masterWalletAddress, string targetWalletAddress, string encodedData) { return(await _transactionRunner.RunWithTransactionAsync(async txContext => { var nonce = await _noncesRepository.GetNextNonceAsync(masterWalletAddress, txContext); var transaction = new Transaction( to: targetWalletAddress, amount: 0, nonce: nonce, gasPrice: _gasPrice, gasLimit: _gasLimit, data: encodedData ); _log.Info ( "Transaction has been built.", new { operationId, masterWalletAddress, nonce } ); try { var(v, r, s) = await _transactionSigner.SignTransactionAsync(masterWalletAddress, transaction.RawHash); var signature = EthECDSASignatureFactory.FromComponents(r: r, s: s, v: v); transaction.SetSignature(signature); #region Logging _log.Info ( "Transaction has been signed.", new { operationId, masterWalletAddress, nonce } ); #endregion } catch (Exception e) { #region Logging _log.Error ( e, "Failed to sign transaction.", new { operationId, masterWalletAddress, nonce } ); #endregion throw; } var transactionData = transaction.GetRLPEncoded().ToHex(true); try { await _operationsRepository.SetTransactionDataAsync( operationId, transactionData, txContext); #region Logging _log.Info ( "Transaction data has been saved.", new { operationId, masterWalletAddress, nonce } ); #endregion } catch (Exception e) { #region Logging _log.Error ( e, "Failed to save transaction data.", new { operationId, masterWalletAddress, nonce } ); #endregion throw; } return transactionData; })); }
public async Task <ReferralStakesStatusUpdateErrorCode> TokensBurnAndReleaseAsync(string referralId, decimal?releaseBurnRatio = null) { if (string.IsNullOrEmpty(referralId)) { throw new ArgumentNullException(nameof(referralId)); } var newStatus = StakeStatus.TokensBurnAndReleaseStarted; var transactionError = await _transactionRunner.RunWithTransactionAsync(async txContext => { var referralStake = await _referralStakesRepository.GetByReferralIdAsync(referralId, txContext); if (referralStake == null) { return(ReferralStakesStatusUpdateErrorCode.DoesNotExist); } if (referralStake.Status != StakeStatus.TokensReservationSucceeded) { return(ReferralStakesStatusUpdateErrorCode.InvalidStatus); } var customerWalletAddress = await _pbfClient.CustomersApi.GetWalletAddress(Guid.Parse(referralStake.CustomerId)); if (customerWalletAddress.Error == CustomerWalletAddressError.CustomerWalletMissing) { return(ReferralStakesStatusUpdateErrorCode.CustomerWalletIsMissing); } var walletBlockStatus = await _walletManagementClient.Api.GetCustomerWalletBlockStateAsync(referralStake.CustomerId); if (walletBlockStatus.Status == CustomerWalletActivityStatus.Blocked) { return(ReferralStakesStatusUpdateErrorCode.CustomerWalletBlocked); } referralStake.Status = newStatus; referralStake.ReleaseBurnRatio = releaseBurnRatio; var burnRatio = releaseBurnRatio ?? referralStake.ExpirationBurnRatio; var(amountToBurn, amountToRelease) = CalculateAmountToBurnAndRelease(referralStake.Amount, burnRatio); var encodedData = _blockchainEncodingService.EncodeDecreaseRequestData(customerWalletAddress.WalletAddress, amountToBurn, amountToRelease); var operationId = await AddBlockchainOperation(encodedData); await _stakesBlockchainDataRepository.UpsertAsync(referralId, operationId, txContext); await _referralStakesRepository.UpdateReferralStakeAsync(referralStake, txContext); return(ReferralStakesStatusUpdateErrorCode.None); }); if (transactionError == ReferralStakesStatusUpdateErrorCode.None) { await PublishStatusUpdatedEvent(referralId, newStatus); } return(transactionError); }
public async Task <ReferralStakeRequestErrorCode> ReferralStakeAsync(ReferralStakeModel model) { #region Validation if (string.IsNullOrEmpty(model.CustomerId)) { throw new ArgumentNullException(nameof(model.CustomerId)); } if (string.IsNullOrEmpty(model.CampaignId)) { throw new ArgumentNullException(nameof(model.CampaignId)); } if (string.IsNullOrEmpty(model.ReferralId)) { throw new ArgumentNullException(nameof(model.ReferralId)); } if (model.Amount <= 0) { return(ReferralStakeRequestErrorCode.InvalidAmount); } if (model.WarningPeriodInDays < 0) { return(ReferralStakeRequestErrorCode.InvalidWarningPeriodInDays); } if (model.StakingPeriodInDays <= 0) { return(ReferralStakeRequestErrorCode.InvalidStakingPeriodInDays); } if (model.StakingPeriodInDays <= model.WarningPeriodInDays) { return(ReferralStakeRequestErrorCode.WarningPeriodShouldSmallerThanStakingPeriod); } if (model.ExpirationBurnRatio < 0 || model.ExpirationBurnRatio > 100) { return(ReferralStakeRequestErrorCode.InvalidBurnRatio); } var existingStake = await _referralStakesRepository.GetByReferralIdAsync(model.ReferralId); if (existingStake != null) { return(ReferralStakeRequestErrorCode.StakeAlreadyExist); } var customerProfile = await _customerProfileClient.CustomerProfiles.GetByCustomerIdAsync(model.CustomerId); if (customerProfile.ErrorCode == CustomerProfileErrorCodes.CustomerProfileDoesNotExist) { return(ReferralStakeRequestErrorCode.CustomerDoesNotExist); } var campaign = await _campaignClient.Campaigns.GetByIdAsync(model.CampaignId); if (campaign.ErrorCode == CampaignServiceErrorCodes.EntityNotFound) { return(ReferralStakeRequestErrorCode.CampaignDoesNotExist); } var walletBlockStatus = await _walletManagementClient.Api.GetCustomerWalletBlockStateAsync(model.CustomerId); if (walletBlockStatus.Status == CustomerWalletActivityStatus.Blocked) { return(ReferralStakeRequestErrorCode.CustomerWalletBlocked); } var isCustomerIdValidGuid = Guid.TryParse(model.CustomerId, out var customerIdGuid); if (!isCustomerIdValidGuid) { return(ReferralStakeRequestErrorCode.InvalidCustomerId); } var customerWalletAddress = await _pbfClient.CustomersApi.GetWalletAddress(customerIdGuid); if (customerWalletAddress.Error == CustomerWalletAddressError.CustomerWalletMissing) { return(ReferralStakeRequestErrorCode.CustomerWalletIsMissing); } var balance = await _pbfClient.CustomersApi.GetBalanceAsync(customerIdGuid); if (balance.Total < model.Amount) { return(ReferralStakeRequestErrorCode.NotEnoughBalance); } #endregion model.Status = StakeStatus.TokensReservationStarted; model.Timestamp = DateTime.UtcNow; var encodedData = _blockchainEncodingService.EncodeStakeRequestData(customerWalletAddress.WalletAddress, model.Amount); var blockchainOperationId = await _pbfClient.OperationsApi.AddGenericOperationAsync(new GenericOperationRequest { Data = encodedData, SourceAddress = _settingsService.GetMasterWalletAddress(), TargetAddress = _settingsService.GetTokenContractAddress() }); return(await _transactionRunner.RunWithTransactionAsync(async txContext => { await _referralStakesRepository.AddAsync(model, txContext); await _stakesBlockchainDataRepository.UpsertAsync(model.ReferralId, blockchainOperationId.OperationId.ToString(), txContext); return ReferralStakeRequestErrorCode.None; })); }
public async Task <bool> DecodeNonDecodedTopicsAsync( int batchSize) { using (_log.BeginScope($"{nameof(DecodeNonDecodedTopicsAsync)}-{Guid.NewGuid()}")) { IReadOnlyCollection <TransactionLog> nonDecodedTransactionLogs; try { #region Logging _log.Debug($"Getting up to {batchSize} non-decoded transaction logs with known ABIs."); #endregion nonDecodedTransactionLogs = (await _transactionRepository.GetNonDecodedTransactionLogsAsync(batchSize)).ToList(); #region Logging _log.Debug($"Got {nonDecodedTransactionLogs.Count} non-decoded transaction logs with known ABIs."); #endregion } catch (Exception e) { #region Logging _log.Error(e, $"Failed to get up to {batchSize} non-decoded transaction logs with known ABIs."); #endregion return(false); } if (nonDecodedTransactionLogs.Count == 0) { #region Logging _log.Debug("No non-decoded transaction logs with known ABIs have been found."); #endregion return(false); } List <Event> decodedEvents; try { #region Logging _log.Debug($"Decoding {nonDecodedTransactionLogs.Count} transaction logs."); #endregion decodedEvents = nonDecodedTransactionLogs.Select(x => _decodingService.DecodeTransactionLog(x)).ToList(); #region Logging _log.Debug($"{nonDecodedTransactionLogs.Count} transaction logs have been decoded."); #endregion } catch (Exception e) { #region Logging _log.Error(e, "Failed to decode transaction logs."); #endregion return(false); } try { await _transactionRunner.RunWithTransactionAsync(async (txContext) => { await _eventRepository.SaveAsync(decodedEvents, txContext); await _transactionRepository.SaveDecodedAsync( nonDecodedTransactionLogs.Select(t => Tuple.Create(t.LogIndex, t.TransactionHash))); }); } catch (InvalidOperationException e) { #region Logging _log.Error(e, "Failed to commit transaction."); #endregion return(false); } catch (Exception e) { #region Logging _log.Error(e, "Failed to save decoded transaction logs."); #endregion return(false); } #region Logging _log.Info($"{nonDecodedTransactionLogs.Count} transaction logs have been decoded."); #endregion return(true); } }
public async Task <LinkingApprovalResultModel> ApproveLinkRequestAsync(string privateAddress, string publicAddress, string signature) { #region Validation if (string.IsNullOrEmpty(privateAddress)) { return(LinkingApprovalResultModel.Failed(LinkingError.InvalidPrivateAddress)); } if (string.IsNullOrEmpty(publicAddress)) { return(LinkingApprovalResultModel.Failed(LinkingError.InvalidPublicAddress)); } if (string.IsNullOrEmpty(signature)) { return(LinkingApprovalResultModel.Failed(LinkingError.InvalidSignature)); } var linkRequest = await _requestsRepository.GetByPrivateAddressAsync(privateAddress); if (linkRequest == null) { return(LinkingApprovalResultModel.Failed(LinkingError.LinkingRequestDoesNotExist)); } if (linkRequest.IsApproved()) { return(LinkingApprovalResultModel.Failed(LinkingError.LinkingRequestAlreadyApproved)); } var walletError = await CheckWalletStatus(linkRequest.CustomerId); if (walletError != LinkingError.None) { return(LinkingApprovalResultModel.Failed(walletError)); } if (!_signatureValidator.Validate(linkRequest.LinkCode, signature, publicAddress)) { return(LinkingApprovalResultModel.Failed(LinkingError.InvalidSignature)); } var fee = await _customersService.GetNextWalletLinkingFeeAsync(Guid.Parse(linkRequest.CustomerId)); if (fee > 0) { var balanceResult = await _pbfClient.CustomersApi.GetBalanceAsync(Guid.Parse(linkRequest.CustomerId)); if (balanceResult.Error != CustomerBalanceError.None) { _log.Error(message: "Couldn't get balance for customer wallet", context: new { customerId = linkRequest.CustomerId, error = balanceResult.Error.ToString() }); return(LinkingApprovalResultModel.Failed(LinkingError.NotEnoughFunds)); } if (balanceResult.Total < fee) { _log.Warning("The balance is not enough to pay wallet linking fee", context: new { balanceTotal = balanceResult.Total, fee }); return(LinkingApprovalResultModel.Failed(LinkingError.NotEnoughFunds)); } } #endregion await _transactionRunner.RunWithTransactionAsync(async txContext => { await _requestsRepository.SetApprovedAsync(linkRequest.CustomerId, publicAddress, signature, fee, txContext); var counter = await _countersRepository.GetAsync(linkRequest.CustomerId, txContext); var newCounterValue = counter?.ApprovalsCounter + 1 ?? 1; await _countersRepository.UpsertAsync(linkRequest.CustomerId, newCounterValue, txContext); }); await PublishLinkCommandAsync(linkRequest.CustomerId, privateAddress, publicAddress, fee); _log.Info("Wallet linking request has been approved", new { linkRequest.CustomerId, publicAddress, fee }); return(LinkingApprovalResultModel.Succeeded()); }