public async Task <IActionResult> SetDefaultAssets([FromBody] UpdateMerchantWalletDefaultAssetsModel request)
        {
            if (!request.Network.HasValue || request.Network == BlockchainType.None)
            {
                return(BadRequest(
                           ErrorResponse.Create(
                               "Invalid network value, possible values are: Bitcoin, Ethereum, EthereumIata")));
            }

            var assetsToValidate = new List <string>();

            if (request.IncomingPaymentDefaults != null)
            {
                assetsToValidate.AddRange(request.IncomingPaymentDefaults);
            }

            if (request.OutgoingPaymentDefaults != null)
            {
                assetsToValidate.AddRange(request.OutgoingPaymentDefaults);
            }

            try
            {
                foreach (string assetToValidateId in assetsToValidate)
                {
                    string lykkeAssetId = await _lykkeAssetsResolver.GetLykkeId(assetToValidateId);

                    if (await _assetsLocalCache.GetAssetByIdAsync(lykkeAssetId) == null)
                    {
                        throw new AssetUnknownException(lykkeAssetId);
                    }
                }

                await _merchantWalletService.SetDefaultAssetsAsync(
                    request.MerchantId,
                    request.Network.Value,
                    request.WalletAddress,
                    request.IncomingPaymentDefaults,
                    request.OutgoingPaymentDefaults);

                return(NoContent());
            }
            catch (AssetUnknownException e)
            {
                _log.ErrorWithDetails(e, new { e.Asset });

                return(NotFound(ErrorResponse.Create($"Asset not found [{e.Asset}]")));
            }
            catch (MerchantWalletNotFoundException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.MerchantId,
                    e.Network,
                    e.WalletAddress
                });

                return(NotFound(ErrorResponse.Create(e.Message)));
            }
        }
示例#2
0
        public async Task <IActionResult> SetAssetGeneralSettings([FromBody] UpdateAssetGeneralSettingsRequest request)
        {
            try
            {
                string lykkeAssetId = await _lykkeAssetsResolver.GetLykkeId(request.AssetDisplayId);

                Asset asset = await _assetsLocalCache.GetAssetByIdAsync(lykkeAssetId);

                if (asset == null)
                {
                    return(NotFound(ErrorResponse.Create($"Asset {request.AssetDisplayId} not found")));
                }

                await _assetSettingsService.SetGeneralAsync(Mapper.Map <AssetGeneralSettings>(request));

                return(NoContent());
            }
            catch (InvalidRowKeyValueException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.Variable,
                    e.Value
                });

                return(NotFound(ErrorResponse.Create("Asset not found")));
            }
            catch (AssetUnknownException e)
            {
                _log.ErrorWithDetails(e, new { e.Asset });

                return(NotFound(ErrorResponse.Create($"Asset {e.Asset} can't be resolved")));
            }
        }
示例#3
0
        public async Task <IActionResult> SetGeneralAssetsSettings([FromBody] UpdateAssetAvailabilityRequest request)
        {
            try
            {
                string lykkeAssetId = await _lykkeAssetsResolver.GetLykkeId(request.AssetId);

                Asset asset = await _assetsLocalCache.GetAssetByIdAsync(lykkeAssetId);

                if (asset == null)
                {
                    return(NotFound(ErrorResponse.Create($"Asset {request.AssetId} not found")));
                }

                await _assetsAvailabilityService.SetGeneralAsync(request.AssetId, request.AvailabilityType,
                                                                 request.Value);

                return(NoContent());
            }
            catch (AssetUnknownException assetEx)
            {
                await _log.WriteErrorAsync(nameof(AssetsController), nameof(SetGeneralAssetsSettings),
                                           new { assetEx.Asset }.ToJson(), assetEx);

                return(NotFound(ErrorResponse.Create($"Asset {assetEx.Asset} can't be resolved")));
            }
            catch (Exception ex)
            {
                await _log.WriteErrorAsync(nameof(AssetsController), nameof(SetGeneralAssetsSettings), ex);

                throw;
            }
        }
示例#4
0
        public async Task <IActionResult> AddRate([FromBody] AddAssetRateModel request)
        {
            try
            {
                string lykkeBaseAssetId = await _lykkeAssetsResolver.GetLykkeId(request.BaseAssetId);

                if (await _assetsLocalCache.GetAssetByIdAsync(lykkeBaseAssetId) == null)
                {
                    return(NotFound(ErrorResponse.Create("Base asset not found")));
                }

                string lykkeQuotingAssetId = await _lykkeAssetsResolver.GetLykkeId(request.QuotingAssetId);

                if (await _assetsLocalCache.GetAssetByIdAsync(lykkeQuotingAssetId) == null)
                {
                    return(NotFound(ErrorResponse.Create("Quoting asset not found")));
                }

                IAssetPairRate newRate = await _assetRatesService.AddAsync(Mapper.Map <AddAssetPairRateCommand>(request, opt =>
                {
                    opt.Items["BaseAssetId"]    = lykkeBaseAssetId;
                    opt.Items["QuotingAssetId"] = lykkeQuotingAssetId;
                }));

                return(Ok(Mapper.Map <AssetRateResponse>(newRate, opt =>
                {
                    opt.Items["BaseAssetId"] = request.BaseAssetId;
                    opt.Items["QuotingAssetId"] = request.QuotingAssetId;
                })));
            }
            catch (AssetUnknownException e)
            {
                _log.ErrorWithDetails(e, new { e.Asset });

                return(NotFound(ErrorResponse.Create($"Asset not found [{e.Asset}]")));
            }
            catch (AssetPairRateStorageNotSupportedException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.BaseAssetId,
                    e.QuotingAssetId
                });

                return(StatusCode((int)HttpStatusCode.NotImplemented, ErrorResponse.Create(e.Message)));
            }
        }
        public async Task <TransferResult> ExecuteAsync(TransferCommand transferCommand)
        {
            BlockchainType blockchainType = await _assetSettingsService.GetNetworkAsync(transferCommand.AssetId);

            IBlockchainApiClient blockchainClient = _blockchainClientProvider.Get(blockchainType);

            BlockchainTransferCommand cmd = new BlockchainTransferCommand(transferCommand.AssetId);

            string lykkeAssetId = transferCommand.AssetId.IsGuid()
                ? transferCommand.AssetId
                : await _lykkeAssetsResolver.GetLykkeId(transferCommand.AssetId);

            foreach (var transferCommandAmount in transferCommand.Amounts)
            {
                decimal balance = await blockchainClient.GetBalanceAsync(transferCommandAmount.Source, lykkeAssetId);

                if (transferCommandAmount.Amount == null)
                {
                    if (balance > 0)
                    {
                        cmd.Amounts.Add(new TransferAmount
                        {
                            Amount      = balance,
                            Source      = transferCommandAmount.Source,
                            Destination = transferCommandAmount.Destination
                        });

                        continue;
                    }

                    throw new InsufficientFundsException(transferCommandAmount.Source, transferCommand.AssetId);
                }

                if (transferCommandAmount.Amount > balance)
                {
                    throw new InsufficientFundsException(transferCommandAmount.Source, transferCommand.AssetId);
                }

                cmd.Amounts.Add(transferCommandAmount);
            }

            BlockchainTransferResult blockchainTransferResult = await blockchainClient.TransferAsync(cmd);

            ITransfer transfer = await _transferRepository.AddAsync(new Transfer
            {
                AssetId      = transferCommand.AssetId,
                Blockchain   = blockchainTransferResult.Blockchain,
                CreatedOn    = DateTime.UtcNow,
                Amounts      = transferCommand.Amounts,
                Transactions = Mapper.Map <IEnumerable <TransferTransaction> >(blockchainTransferResult.Transactions)
            });

            return(Mapper.Map <TransferResult>(transfer));
        }
        public async Task ValidateTransfer(string walletAddress, string assetId, decimal transferAmount)
        {
            assetId = assetId.IsGuid() ? assetId : await _lykkeAssetsResolver.GetLykkeId(assetId);

            BlockchainType network = await _assetSettingsService.GetNetworkAsync(assetId);

            IBlockchainApiClient blockchainApiClient = _blockchainClientProvider.Get(network);

            decimal balance = await blockchainApiClient.GetBalanceAsync(walletAddress, assetId);

            if (balance < transferAmount)
            {
                throw new InsufficientFundsException(walletAddress, assetId);
            }
        }
示例#7
0
        public async Task <IActionResult> ResolvePaymentAssets(string merchantId, [FromQuery] string settlementAssetId)
        {
            IMerchant merchant = await _merchantService.GetAsync(merchantId);

            if (merchant == null)
            {
                return(NotFound(ErrorResponse.Create("Couldn't find merchant")));
            }

            string lykkeAssetId = await _lykkeAssetsResolver.GetLykkeId(settlementAssetId);

            Asset asset = await _assetsLocalCache.GetAssetByIdAsync(lykkeAssetId);

            if (asset == null)
            {
                return(NotFound(ErrorResponse.Create("Couldn't find asset")));
            }

            try
            {
                IReadOnlyList <string> assets =
                    await _assetsAvailabilityService.ResolvePaymentAsync(merchantId, settlementAssetId);

                return(Ok(new AvailableAssetsResponseModel {
                    Assets = assets
                }));
            }
            catch (AssetUnknownException assetEx)
            {
                await _log.WriteErrorAsync(nameof(MerchantsController), nameof(ResolvePaymentAssets),
                                           new { assetEx.Asset }.ToJson(), assetEx);

                return(BadRequest(ErrorResponse.Create(assetEx.Message)));
            }
            catch (Exception ex)
            {
                await _log.WriteErrorAsync(nameof(MerchantsController), nameof(ResolvePaymentAssets), new
                {
                    merchantId,
                    settlementAssetId
                }.ToJson(), ex);

                throw;
            }
        }
        public async Task <IActionResult> ResolvePaymentAssets(string merchantId, [FromQuery] string settlementAssetId)
        {
            merchantId = Uri.UnescapeDataString(merchantId);

            if (string.IsNullOrEmpty(settlementAssetId))
            {
                return(BadRequest(ErrorResponse.Create("Settlement asset id is invalid")));
            }

            try
            {
                string lykkeAssetId = await _lykkeAssetsResolver.GetLykkeId(settlementAssetId);

                Asset asset = await _assetsLocalCache.GetAssetByIdAsync(lykkeAssetId);

                if (asset == null)
                {
                    return(NotFound(ErrorResponse.Create("Couldn't find asset")));
                }

                IReadOnlyList <string> assets =
                    await _assetSettingsService.ResolvePaymentAsync(merchantId, settlementAssetId);

                return(Ok(new AvailableAssetsResponseModel {
                    Assets = assets
                }));
            }
            catch (InvalidRowKeyValueException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.Variable,
                    e.Value
                });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
            catch (AssetUnknownException e)
            {
                _log.ErrorWithDetails(e, new { e.Asset });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
        }
示例#9
0
        private async Task <(string AssetPairId, decimal PaymentAmount, decimal Rate)> GetPaymentInfoAsync(string settlementAssetId, string paymentAssetId, decimal amount, string merchantId, RequestMarkup requestMarkup)
        {
            string lykkePaymentAssetId = await _lykkeAssetsResolver.GetLykkeId(paymentAssetId);

            string lykkeSettlementAssetId = await _lykkeAssetsResolver.GetLykkeId(settlementAssetId);

            string assetPairId = $"{paymentAssetId}{settlementAssetId}";

            IMarkup merchantMarkup;

            try
            {
                merchantMarkup = await _markupService.ResolveAsync(merchantId, assetPairId);
            }
            catch (MarkupNotFoundException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.MerchantId,
                    e.AssetPairId
                });

                throw;
            }

            decimal paymentAmount, rate;

            try
            {
                paymentAmount = await _calculationService.GetAmountAsync(lykkePaymentAssetId,
                                                                         lykkeSettlementAssetId, amount, requestMarkup, merchantMarkup);

                rate = await _calculationService.GetRateAsync(lykkePaymentAssetId, lykkeSettlementAssetId,
                                                              requestMarkup.Percent, requestMarkup.Pips, merchantMarkup);
            }
            catch (MarketPriceZeroException e)
            {
                _log.ErrorWithDetails(e, new { e.PriceType });

                throw;
            }
            catch (UnexpectedAssetPairPriceMethodException e)
            {
                _log.ErrorWithDetails(e, new { e.PriceMethod });

                throw;
            }
            catch (ZeroRateException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    lykkePaymentAssetId,
                    lykkeSettlementAssetId,
                    requestMarkup.Percent,
                    requestMarkup.Pips,
                    merchantMarkup
                });

                throw;
            }

            return(assetPairId, paymentAmount, rate);
        }
示例#10
0
        public async Task <IOrder> GetLatestOrCreateAsync(IPaymentRequest paymentRequest, bool force = false)
        {
            IReadOnlyList <IOrder> orders = await _orderRepository.GetAsync(paymentRequest.Id);

            IOrder latestOrder = orders.OrderByDescending(x => x.ExtendedDueDate).FirstOrDefault();

            var now = DateTime.UtcNow;

            if (latestOrder != null)
            {
                if (now < latestOrder.DueDate)
                {
                    return(latestOrder);
                }

                if (now < latestOrder.ExtendedDueDate && !force)
                {
                    return(latestOrder);
                }
            }

            IMerchant merchant = await _merchantRepository.GetAsync(paymentRequest.MerchantId);

            if (merchant == null)
            {
                throw new MerchantNotFoundException(paymentRequest.MerchantId);
            }

            string lykkePaymentAssetId = await _lykkeAssetsResolver.GetLykkeId(paymentRequest.PaymentAssetId);

            string lykkeSettlementAssetId = await _lykkeAssetsResolver.GetLykkeId(paymentRequest.SettlementAssetId);

            string assetPairId = $"{paymentRequest.PaymentAssetId}{paymentRequest.SettlementAssetId}";

            RequestMarkup requestMarkup = Mapper.Map <RequestMarkup>(paymentRequest);

            IMarkup merchantMarkup;

            try
            {
                merchantMarkup = await _markupService.ResolveAsync(merchant.Id, assetPairId);
            }
            catch (MarkupNotFoundException ex)
            {
                await _log.WriteErrorAsync(nameof(OrderService), nameof(GetLatestOrCreateAsync), new
                {
                    ex.MerchantId,
                    ex.AssetPairId
                }.ToJson(), ex);

                throw;
            }

            decimal paymentAmount, rate;

            try
            {
                paymentAmount = await _calculationService.GetAmountAsync(lykkePaymentAssetId,
                                                                         lykkeSettlementAssetId, paymentRequest.Amount, requestMarkup, merchantMarkup);

                rate = await _calculationService.GetRateAsync(lykkePaymentAssetId, lykkeSettlementAssetId,
                                                              requestMarkup.Percent, requestMarkup.Pips, merchantMarkup);
            }
            catch (MarketPriceZeroException priceZeroEx)
            {
                _log.WriteError(nameof(GetLatestOrCreateAsync), new { priceZeroEx.PriceType }, priceZeroEx);

                throw;
            }
            catch (UnexpectedAssetPairPriceMethodException assetPairEx)
            {
                _log.WriteError(nameof(GetLatestOrCreateAsync), new { assetPairEx.PriceMethod }, assetPairEx);

                throw;
            }

            var order = new Order
            {
                MerchantId       = paymentRequest.MerchantId,
                PaymentRequestId = paymentRequest.Id,
                AssetPairId      = assetPairId,
                SettlementAmount = paymentRequest.Amount,
                PaymentAmount    = paymentAmount,
                DueDate          = now.Add(_orderExpirationPeriods.Primary),
                ExtendedDueDate  = now.Add(_orderExpirationPeriods.Extended),
                CreatedDate      = now,
                ExchangeRate     = rate
            };

            IOrder createdOrder = await _orderRepository.InsertAsync(order);

            await _log.WriteInfoAsync(nameof(OrderService), nameof(GetLatestOrCreateAsync), order.ToJson(),
                                      "Order created.");

            return(createdOrder);
        }
示例#11
0
        public async Task <BlockchainTransferResult> TransferAsync(BlockchainTransferCommand transfer)
        {
            BlockchainTransferResult result = new BlockchainTransferResult {
                Blockchain = BlockchainType.Ethereum
            };

            string lykkeAssetId = await _lykkeAssetsResolver.GetLykkeId(transfer.AssetId);

            Asset asset = await _assetsLocalCache.GetAssetByIdAsync(lykkeAssetId);

            if (asset.Type != AssetType.Erc20Token || !asset.IsTradable)
            {
                throw new AssetNotSupportedException(asset.Name);
            }

            ListOfErc20Token tokenSpecification =
                await _assetsService.Erc20TokenGetBySpecificationAsync(new Erc20TokenSpecification { Ids = new[] { asset.Id } });

            string tokenAddress = tokenSpecification?.Items.SingleOrDefault(x => x.AssetId == asset.Id)?.Address;

            foreach (TransferAmount transferAmount in transfer.Amounts)
            {
                var transferRequest = Mapper.Map <TransferFromDepositRequest>(transferAmount,
                                                                              opts => opts.Items["TokenAddress"] = tokenAddress);

                object response = await _ethereumServiceClient.ApiLykkePayErc20depositsTransferPostAsync(
                    _ethereumSettings.ApiKey, transferRequest);

                var errorMessage = string.Empty;

                var operationId = string.Empty;

                if (response is ApiException ex)
                {
                    await _log.WriteWarningAsync(nameof(TransferAsync), transferAmount.ToJson(), ex.Error?.ToJson());

                    errorMessage = ex.Error?.Message;
                }
                else if (response is OperationIdResponse op)
                {
                    operationId = op.OperationId;
                }
                else
                {
                    throw new UnrecognizedApiResponse(response?.GetType().FullName ?? "Response object is null");
                }

                result.Transactions.Add(new BlockchainTransactionResult
                {
                    Amount       = transferAmount.Amount,
                    AssetId      = asset.Name,
                    Hash         = string.Empty,
                    IdentityType = TransactionIdentityType.Specific,
                    Identity     = operationId,
                    Sources      = new List <string> {
                        transferAmount.Source
                    },
                    Destinations = new List <string> {
                        transferAmount.Destination
                    },
                    Error = errorMessage
                });
            }

            return(result);
        }
        public async Task <BlockchainTransferResult> TransferAsync(BlockchainTransferCommand transfer)
        {
            BlockchainTransferResult result = new BlockchainTransferResult {
                Blockchain = BlockchainType.EthereumIata
            };

            string lykkeAssetId = await _lykkeAssetsResolver.GetLykkeId(transfer.AssetId);

            Asset asset = await _assetsLocalCache.GetAssetByIdAsync(lykkeAssetId);

            if (asset.Type != AssetType.Erc20Token)
            {
                throw new AssetNotSupportedException(asset.Name);
            }

            ListOfErc20Token tokenSpecification =
                await _assetsService.Erc20TokenGetBySpecificationAsync(new Erc20TokenSpecification
                                                                       { Ids = new[] { asset.Id } });

            string tokenAddress = tokenSpecification?.Items.SingleOrDefault(x => x.AssetId == asset.Id)?.Address;

            foreach (TransferAmount transferAmount in transfer.Amounts)
            {
                var transferRequest = Mapper.Map <AirlinesTransferFromDepositRequest>(transferAmount,
                                                                                      opts =>
                {
                    opts.Items["TokenAddress"]    = tokenAddress;
                    opts.Items["AssetMultiplier"] = asset.MultiplierPower;
                    opts.Items["AssetAccuracy"]   = asset.Accuracy;
                });

                object response = await _retryPolicy.ExecuteAsync(() => InvokeTransfer(transferRequest));

                var ex = response as ApiException;

                var operation = response as OperationIdResponse;

                if (ex != null)
                {
                    _log.Warning(ex.Error?.Message);
                }

                if (ex == null && operation == null)
                {
                    throw new UnrecognizedApiResponse(response?.GetType().FullName ?? "Response object is null");
                }

                result.Transactions.Add(new BlockchainTransactionResult
                {
                    Amount       = transferAmount.Amount ?? 0,
                    AssetId      = asset.DisplayId,
                    Hash         = string.Empty,
                    IdentityType = TransactionIdentityType.Specific,
                    Identity     = operation?.OperationId ?? string.Empty,
                    Sources      = new List <string> {
                        transferAmount.Source
                    },
                    Destinations = new List <string> {
                        transferAmount.Destination
                    },
                    Error     = ex?.Error?.Message ?? string.Empty,
                    ErrorType = ex.GetDomainError()
                });
            }

            return(result);
        }
 public string Resolve(object source, object destination, string sourceMember, string destMember, ResolutionContext context)
 {
     return(_lykkeAssetsResolver.GetLykkeId(sourceMember).GetAwaiter().GetResult());
 }