コード例 #1
0
        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);
                }
            }
        }
コード例 #3
0
        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);
            });
        }
コード例 #4
0
 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
     });
 }
コード例 #5
0
 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");
            }
        }
コード例 #10
0
 public static void SetAdditionalData(this IOffchainTransfer transfer, OffchainTransferAdditionalData model)
 {
     transfer.AdditionalDataJson = model.ToJson();
 }
コード例 #11
0
        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);
        }
コード例 #12
0
        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);
        }
コード例 #13
0
        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
            });
        }