public static OffchainTransferAdditionalData GetAdditionalData(this IOffchainTransfer transfer) { if (string.IsNullOrWhiteSpace(transfer.AdditionalDataJson)) { return(new OffchainTransferAdditionalData()); } return(JsonConvert.DeserializeObject <OffchainTransferAdditionalData>(transfer.AdditionalDataJson)); }
private async Task FinalizeSwap(IBitcoinTransaction transaction, IOffchainTransfer offchainTransfer) { var transactionsContextData = new Dictionary <string, SwapOffchainContextData>(); var allTransfers = new HashSet <string>(offchainTransfer.GetAdditionalData().ChildTransfers) { offchainTransfer.Id }; foreach (var transferId in allTransfers) { try { var transfer = await _offchainTransferRepository.GetTransfer(transferId); if (!transactionsContextData.ContainsKey(transfer.OrderId)) { var ctx = await _bitcoinTransactionService.GetTransactionContext <SwapOffchainContextData>(transaction.TransactionId); if (ctx == null) { continue; } transactionsContextData.Add(transfer.OrderId, ctx); } var contextData = transactionsContextData[transfer.OrderId]; var operation = contextData.Operations.FirstOrDefault(x => x.TransactionId == transferId); if (operation == null) { continue; } if (string.IsNullOrWhiteSpace(operation?.ClientTradeId) || string.IsNullOrWhiteSpace(operation?.ClientId)) { await _log.WriteWarningAsync(nameof(OffchainTransactionFinalizeFunction), nameof(FinalizeSwap), operation?.ToJson(), $"Missing fields. Client trade id {operation?.ClientTradeId}, client {operation?.ClientId}, transfer: {transferId}"); continue; } await Task.WhenAll( _offchainTransferRepository.CompleteTransfer(transferId), _clientTradesRepository.SetIsSettledAsync(operation.ClientId, operation.ClientTradeId, true) ); } catch (Exception e) { await _log.WriteErrorAsync(nameof(OffchainTransactionFinalizeFunction), nameof(FinalizeSwap), $"Transfer: {transferId}", e); } } }
public async Task AddChildTransfer(string transferId, IOffchainTransfer child) { await _storage.MergeAsync(OffchainTransferEntity.ByCommon.GeneratePartitionKey(), transferId, entity => { var data = entity.GetAdditionalData(); data.ChildTransfers.Add(child.Id); entity.SetAdditionalData(data); entity.Amount += child.Amount; return(entity); }); }
public static OffchainTransferEntity Create(IOffchainTransfer transfer) { return(new OffchainTransferEntity { BsonId = transfer.TransferId.ToString(), TransferId = transfer.TransferId, CreateDt = transfer.CreateDt, Required = transfer.Required, AssetId = transfer.AssetId, Multisig = transfer.Multisig, Completed = transfer.Completed, Closed = transfer.Closed }); }
public static OffchainTransferEntity Create(IOffchainTransfer commonTransfer) { return(new OffchainTransferEntity { PartitionKey = commonTransfer.ClientId, RowKey = commonTransfer.Id, AssetId = commonTransfer.AssetId, Amount = commonTransfer.Amount, ClientId = commonTransfer.ClientId, Type = commonTransfer.Type, OrderId = commonTransfer.OrderId, CreatedDt = commonTransfer.CreatedDt, ChannelClosing = commonTransfer.ChannelClosing, Onchain = commonTransfer.Onchain }); }
private async Task FinalizeCashOut(IBitcoinTransaction transaction, IOffchainTransfer offchainTransfer) { var amount = Math.Abs((double)offchainTransfer.Amount); var data = await _exchangeOperationsService.FinishCashOutAsync(transaction.TransactionId, offchainTransfer.ClientId, (double)offchainTransfer.Amount, offchainTransfer.AssetId); await CreateHubCashoutIfNeed(offchainTransfer); if (!data.IsOk()) { await _log.WriteWarningAsync("CashOutController", "CashOut", data.ToJson(), "ME operation failed"); await _srvSlackNotifications.SendNotification(ChannelTypes.Errors, $"Cashout failed in ME, client: {offchainTransfer.ClientId}, transfer: {transaction.TransactionId}, ME code result: {data.Code}"); } var contextData = await _bitcoinTransactionService.GetTransactionContext <CashOutContextData>(transaction.TransactionId); var swiftData = contextData.AddData?.SwiftData; if (swiftData != null) { await _cashOutAttemptRepository.SetIsSettledOffchain(contextData.ClientId, swiftData.CashOutRequestId); } else { if (offchainTransfer.AssetId == LykkeConstants.SolarAssetId) { await PostSolarCashOut(offchainTransfer.ClientId, contextData.Address, amount, transaction.TransactionId); } else if (offchainTransfer.AssetId == LykkeConstants.ChronoBankAssetId) { await PostChronoBankCashOut(contextData.Address, amount, transaction.TransactionId); } else if (offchainTransfer.AssetId == LykkeConstants.QuantaAssetId) { await PostQuantaCashOut(contextData.Address, amount, transaction.TransactionId); } else { var clientData = await _personalDataService.GetAsync(contextData.ClientId); await _srvEmailsFacade.SendNoRefundOCashOutMail(clientData.Email, contextData.Amount, contextData.AssetId, transaction.BlockchainHash); } } }
private async Task CreateHubCashoutIfNeed(IOffchainTransfer offchainTransfer) { try { var client = await _walletCredentialsRepository.GetAsync(offchainTransfer.ClientId); var currentRequests = (await _offchainRequestRepository.GetRequestsForClient(offchainTransfer.ClientId)).ToList(); var currentChannels = await _bitcoinApiClient.Balances(client.MultiSig); var hasBtcRequest = currentRequests.FirstOrDefault(x => x.AssetId == LykkeConstants.BitcoinAssetId && x.TransferType == OffchainTransferType.HubCashout) != null; var hasLkkRequest = currentRequests.FirstOrDefault(x => x.AssetId == LykkeConstants.LykkeAssetId && x.TransferType == OffchainTransferType.HubCashout) != null; var btcSetting = await GetAssetSetting(LykkeConstants.BitcoinAssetId); var lkkSetting = await GetAssetSetting(LykkeConstants.LykkeAssetId); var btcHubAmount = !currentChannels.HasError && currentChannels.Balances.ContainsKey(LykkeConstants.BitcoinAssetId) && currentChannels.Balances[LykkeConstants.BitcoinAssetId].Actual ? currentChannels.Balances[LykkeConstants.BitcoinAssetId].HubAmount : 0; var lkkHubAmount = !currentChannels.HasError && currentChannels.Balances.ContainsKey(LykkeConstants.LykkeAssetId) && currentChannels.Balances[LykkeConstants.LykkeAssetId].Actual ? currentChannels.Balances[LykkeConstants.LykkeAssetId].HubAmount : 0; var needBtcCashout = offchainTransfer.AssetId != LykkeConstants.BitcoinAssetId && btcHubAmount > btcSetting.Dust && !hasBtcRequest; var needLkkCashout = offchainTransfer.AssetId != LykkeConstants.LykkeAssetId && lkkHubAmount > lkkSetting.Dust && !hasLkkRequest; await _offchainRequestService.CreateHubCashoutRequests(offchainTransfer.ClientId, needBtcCashout?btcHubAmount : 0, needLkkCashout?lkkHubAmount : 0); } catch (Exception e) { await _log.WriteErrorAsync(nameof(OffchainTransactionFinalizeFunction), nameof(CreateHubCashoutIfNeed), $"{offchainTransfer.ClientId}", e); } }
private async Task FinalizeTransfer(IBitcoinTransaction transaction, IOffchainTransfer transfer) { var contextData = await _bitcoinTransactionService.GetTransactionContext <TransferContextData>(transaction.TransactionId); switch (contextData.TransferType) { case TransferType.ToMarginAccount: await FinalizeTransferToMargin(contextData, transfer); return; case TransferType.Common: await FinalizeCommonTransfer(transaction, contextData); return; default: throw new ArgumentOutOfRangeException(); } }
private async Task FinalizeTransferToMargin(TransferContextData context, IOffchainTransfer transfer) { var sourceTransferContext = context.Transfers.FirstOrDefault(x => x.ClientId == transfer.ClientId); var destTransferContext = context.Transfers.FirstOrDefault(x => x.ClientId != transfer.ClientId); if (sourceTransferContext?.Actions?.UpdateMarginBalance == null) { throw new Exception(); } await _exchangeOperationsService.FinishTransferAsync( transfer.Id, sourceTransferContext.ClientId, destTransferContext.ClientId, (double)transfer.Amount, transfer.AssetId); var marginDataService = _marginDataServiceResolver.Resolve(false); var depositToMarginResult = await marginDataService.DepositToAccount(transfer.ClientId, sourceTransferContext.Actions.UpdateMarginBalance.AccountId, sourceTransferContext.Actions.UpdateMarginBalance.Amount, MarginPaymentType.Transfer); if (depositToMarginResult) { await _marginTradingPaymentLog.CreateAsync(MarginTradingPaymentLog.CreateOk(transfer.ClientId, sourceTransferContext.Actions.UpdateMarginBalance.AccountId, DateTime.UtcNow, sourceTransferContext.Actions.UpdateMarginBalance.Amount, transfer.ExternalTransferId)); } else { var errorLog = MarginTradingPaymentLog.CreateError(transfer.ClientId, sourceTransferContext.Actions.UpdateMarginBalance.AccountId, DateTime.UtcNow, sourceTransferContext.Actions.UpdateMarginBalance.Amount, "Error deposit to margin account"); await _marginTradingPaymentLog.CreateAsync(errorLog); await _srvSlackNotifications.SendNotification(ChannelTypes.MarginTrading, errorLog.ToJson(), "Transaction handler"); } }
public static void SetAdditionalData(this IOffchainTransfer transfer, OffchainTransferAdditionalData model) { transfer.AdditionalDataJson = model.ToJson(); }
private async Task <OffchainResult> CreateChannel(IWalletCredentials credentials, IOffchainTransfer offchainTransfer, bool required) { if (offchainTransfer == null || offchainTransfer.ClientId != credentials.ClientId || offchainTransfer.Completed) { throw new OffchainException(ErrorCode.Exception, offchainTransfer?.AssetId); } var fromClient = offchainTransfer.Type == OffchainTransferType.DirectTransferFromClient; var result = await _bitcoinApiClient.CreateChannelAsync(new CreateChannelData { AssetId = offchainTransfer.AssetId, ClientPubKey = credentials.PublicKey, ClientAmount = fromClient ? offchainTransfer.Amount : 0, HubAmount = 0, Required = required, ExternalTransferId = offchainTransfer.ExternalTransferId }); var offchainTransferInfo = (new { offchainTransfer.ClientId, Asset = offchainTransfer.AssetId, offchainTransfer.Amount, offchainTransfer.Type }).ToJson(); if (!result.HasError) { await _offchainTransferRepository.UpdateTransfer(offchainTransfer.Id, result.TransferId?.ToString(), closing : result.ChannelClosing, onchain : true); var offchainResult = new OffchainResult { TransferId = offchainTransfer.Id, TransactionHex = result.Transaction, OperationResult = result.ChannelClosing ? OffchainOperationResult.Transfer : OffchainOperationResult.CreateChannel }; await _logger.WriteInfoAsync("CreateChannel", offchainTransferInfo, $"Offchain channel successfully created: {(new { offchainResult.TransferId, offchainResult.OperationResult }).ToJson()}"); return(offchainResult); } await _logger.WriteErrorAsync("CreateChannel", offchainTransferInfo, new Exception($"{result.Error.Message}, Code: {result.Error.Code} ")); throw new OffchainException(result.Error.ErrorCode, result.Error.Message, result.Error.Code, offchainTransfer.AssetId); }
private async Task <OffchainResult> InternalErrorProcessing(string process, ErrorResponse error, IWalletCredentials credentials, IOffchainTransfer offchainTransfer, bool required) { if (error.ErrorCode == ErrorCode.ShouldOpenNewChannel) { return(await CreateChannel(credentials, offchainTransfer, required)); } var offchainTransferInfo = (new { offchainTransfer.ClientId, Asset = offchainTransfer.AssetId, offchainTransfer.Amount, offchainTransfer.Type }).ToJson(); await _logger.WriteErrorAsync(process, offchainTransferInfo, new Exception($"{error.Message}, Code: {error.Code}")); throw new OffchainException(error.ErrorCode, error.Message, error.Code, offchainTransfer.AssetId); }
private async Task <OffchainResult> ProcessTransfer(IWalletCredentials credentials, IOffchainTransfer offchainTransfer, string revokePubKey, string encryptedRevokePrivakeKey, string signedCommitment) { var result = await _bitcoinApiClient.Finalize(new FinalizeData { ClientPubKey = credentials.PublicKey, AssetId = offchainTransfer.AssetId, ClientRevokePubKey = revokePubKey, SignedByClientHubCommitment = signedCommitment, ExternalTransferId = offchainTransfer.ExternalTransferId, OffchainTransferId = offchainTransfer.Id }); await _offchainEncryptedKeysRepository.UpdateKey(credentials.ClientId, offchainTransfer.AssetId, encryptedRevokePrivakeKey); if (result.HasError) { return(await InternalErrorProcessing(nameof(ProcessTransfer), result.Error, credentials, offchainTransfer, required : false)); } await _offchainTransferRepository.CompleteTransfer(offchainTransfer.Id, blockchainHash : result.TxHash); await _offchainFinalizeCommandProducer.ProduceFinalize(offchainTransfer.Id, credentials.ClientId, result.TxHash); return(new OffchainResult { TransferId = offchainTransfer.Id, TransactionHex = "0x0", //result.Transaction, OperationResult = OffchainOperationResult.ClientCommitment }); }