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))); } }
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"))); } }
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; } }
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); } }
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))); } }
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); }
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); }
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()); }