コード例 #1
0
        public async Task <AmountAndPrice> GetConvertedAmountAndPriceAsync(string fromAssetId, string toAssetId, double fromAmount)
        {
            if (string.IsNullOrEmpty(fromAssetId))
            {
                throw new ArgumentNullException(nameof(fromAssetId));
            }

            if (string.IsNullOrEmpty(toAssetId))
            {
                throw new ArgumentNullException(nameof(toAssetId));
            }

            if (fromAssetId == toAssetId)
            {
                return(new AmountAndPrice(fromAmount, 1, 1));
            }

            var toAsset = await _assetsDict.GetItemAsync(toAssetId);

            var assetPair = (await _assetPairsDict.Values()).PairWithAssets(fromAssetId, toAssetId);

            var feedData = await _bestPriceRepository.GetAsync(assetPair.Id);

            var price         = (assetPair.BaseAssetId == fromAssetId ? feedData.Ask : 1 / feedData.Ask).TruncateDecimalPlaces(assetPair.Accuracy);
            var invertedPrice = (assetPair.BaseAssetId == fromAssetId ? 1 / feedData.Ask : feedData.Ask).TruncateDecimalPlaces(assetPair.InvertedAccuracy);

            return(new AmountAndPrice((price * fromAmount).TruncateDecimalPlaces(toAsset.Accuracy), price, invertedPrice));
        }
        private async Task <IAssetSetting> GetAssetSetting(string asset)
        {
            var setting = await _assetSettings.GetItemAsync(asset) ?? await _assetSettings.GetItemAsync(LykkeConstants.DefaultAssetSetting);

            if (setting == null)
            {
                throw new Exception($"Setting is not found for {asset}");
            }
            return(setting);
        }
コード例 #3
0
        public async Task <double> GetLkkSoldAmount()
        {
            var lkk = await _assetsDictionary.GetItemAsync(LykkeConstants.LykkeAssetId);

            var walletsToTrack = await _lkkSourceWalletsRepository.GetRecordsAsync();

            double result = 0;

            var balancesTasks =
                walletsToTrack.Select(
                    x =>
                    _srvBlockchainReader.GetBalanceForAdress(x.Address, lkk)
                    .ContinueWith(task => x.StartBalance - task.Result.Balance));

            var balances = await Task.WhenAll(balancesTasks);

            result += balances.Sum();

            result += (await _appGlobalSettingsRepositry.GetAsync()).IcoLkkSold;

            var maxValue =
                (await _tempDataRepository.RetrieveData <IcoCoinsBoughtData>())?.MaxValue ?? 0;

            if (result > maxValue)
            {
                await _tempDataRepository.InsertOrReplaceDataAsync(new IcoCoinsBoughtData { MaxValue = result });

                maxValue = result;
            }

            return(maxValue);
        }
コード例 #4
0
        private async Task <IAsset> GetAsset(string assetId)
        {
            var asset = await _assetRepository.GetItemAsync(assetId);

            if (asset == null)
            {
                throw new BackendException("Provided asset is missing in database", ErrorCode.AssetNotFound);
            }
            return(asset);
        }
コード例 #5
0
        public async Task <IActionResult> Transfer([FromBody] TransferRequest model)
        {
            if (model.Amount <= 0)
            {
                throw new BackendException("Amount can't be less or equal to zero", ErrorCode.BadInputParameter);
            }

            await ValidateAddress(model.SourceAddress);
            await ValidateAddress(model.DestinationAddress);

            var asset = await _assetRepository.GetItemAsync(model.Asset);

            if (asset == null)
            {
                throw new BackendException("Provided asset is missing in database", ErrorCode.AssetNotFound);
            }

            var transactionId = await _builder.AddTransactionId(model.TransactionId, $"Transfer: {model.ToJson()}");

            await _transactionQueueWriter.AddCommand(transactionId, TransactionCommandType.Transfer, new TransferCommand
            {
                Amount             = model.Amount,
                SourceAddress      = model.SourceAddress,
                Asset              = model.Asset,
                DestinationAddress = model.DestinationAddress
            }.ToJson());

            return(Ok(new TransactionIdResponse
            {
                TransactionId = transactionId
            }));
        }
コード例 #6
0
        public async Task <Asset> GetBaseAssetForClient(string clientId, bool isIosDevice, string partnerId)
        {
            var baseAsset = (await _clientAccountSettingsClient.GetBaseAssetAsync(clientId)).BaseAssetId;

            if (string.IsNullOrEmpty(baseAsset))
            {
                var assetsForClient = (await GetAssetsForClient(clientId, isIosDevice, partnerId)).Where(x => x.IsBase);

                baseAsset = assetsForClient.GetFirstAssetId();
            }

            return(await _cachedAssetsDictionary.GetItemAsync(baseAsset));
        }
コード例 #7
0
        private async void AttachSenderTransferToRefLink(IReferralLink refLink, string transferId)
        {
            var transfer = await _offchainTransferRepository.GetTransfer(transferId);

            refLink.Amount = (double)transfer.Amount;
            refLink.Asset  = (await _assets.GetItemAsync(transfer.AssetId)).Id;
            refLink.SenderOffchainTransferId = transferId;
            refLink.State = ReferralLinkState.SentToLykkeSharedWallet.ToString();

            await _referralLinksService.UpdateAsync(refLink);

            await LogInfo(new { RefLink = refLink, TransferId = transferId }, ControllerContext, $"Transfer complete for ref link id {refLink.Id} with amount {transfer.Amount} and asset Id {refLink.Asset}. Offchain transfer Id {transferId} attached with ref link. ");
        }
コード例 #8
0
        public async Task ProcessMessage(BroadcastCommitmentMessage msg, QueueTriggeringContext context)
        {
            var asset = await _assetCache.GetItemAsync(msg.Asset);

            if (asset == null)
            {
                msg.Error = "Asset is not found";
                context.MoveMessageToPoison(msg.ToJson());
                return;
            }

            await _offchainService.BroadcastCommitment(msg.Multisig, asset, msg.UseFees);
        }
コード例 #9
0
        public async Task <double> GetCapitalization(string market)
        {
            double rate = 1;

            if (market != LykkeConstants.LykkeAssetId)
            {
                var assetPairs = await _assetPairsDict.Values();

                var pair = assetPairs.PairWithAssets(LykkeConstants.LykkeAssetId, market);

                if (pair == null)
                {
                    return(0);
                }

                rate = await _srvRatesHelper.GetRate(market, pair);
            }

            CacheRecord record;
            var         asset = await _assetsDict.GetItemAsync(market);

            var cacheKey = GetMarketCapitalizationCacheKey();

            if (!_memCache.TryGetValue(cacheKey, out record))
            {
                double amount = 0;

                await _walletsRepository.GetWalletsByChunkAsync(pairs =>
                {
                    var c =
                        pairs.Select(x => x.Value?.FirstOrDefault(y => y.AssetId == LykkeConstants.LykkeAssetId))
                        .Sum(x => x?.Balance ?? 0);
                    amount += c;
                    return(Task.CompletedTask);
                });

                record = record ?? new CacheRecord();

                record.AssetId = LykkeConstants.LykkeAssetId;
                record.Dt      = DateTime.UtcNow;
                record.Amount  = amount;

                var cacheEntryOptions = new MemoryCacheEntryOptions()
                                        .SetAbsoluteExpiration(_cacheExpTime);

                _memCache.Set(cacheKey, record, cacheEntryOptions);
            }

            return((record.Amount * rate).TruncateDecimalPlaces(asset.Accuracy));
        }
コード例 #10
0
        private Task GenerateColorOutputs(string assetId)
        {
            return(Retry.Try(async() =>
            {
                var asset = await _assetRepostory.GetItemAsync(assetId);
                var setting = await GetAssetSetting(assetId);
                var hotWallet = OpenAssetsHelper.ParseAddress(setting.HotWallet);

                var assetIdObj = new BitcoinAssetId(asset.BlockChainAssetId).AssetId;

                var outputs = await _bitcoinOutputsService.GetColoredUnspentOutputs(setting.HotWallet, assetIdObj, 0, false);

                var balance = outputs.Aggregate(new AssetMoney(assetIdObj, 0), (accum, coin) => accum + coin.Amount);
                var outputSize = new AssetMoney(assetIdObj, setting.OutputSize, asset.MultiplierPower);


                var changeBalance = new AssetMoney(assetIdObj);
                if (setting.ChangeWallet != setting.HotWallet && !string.IsNullOrEmpty(setting.ChangeWallet))
                {
                    var changeOutputs = await _bitcoinOutputsService.GetColoredUnspentOutputs(setting.ChangeWallet, assetIdObj, 0, false);
                    changeBalance = changeOutputs.Aggregate(new AssetMoney(assetIdObj, 0), (accum, coin) => accum + coin.Amount);
                }

                if ((balance + changeBalance).ToDecimal(asset.MultiplierPower) < setting.MinBalance)
                {
                    await SendBalanceNotifications(assetId, setting.HotWallet, setting.MinBalance);
                }

                var existingCoinsCount = outputs.Count(o => o.Amount <= outputSize && o.Amount.Quantity > outputSize.Quantity / 2);

                if (existingCoinsCount > setting.MinOutputsCount)
                {
                    return;
                }

                var generateCnt = setting.MaxOutputsCount - existingCoinsCount;

                var coins = outputs.Where(o => o.Amount > outputSize * 2).ToList();

                balance = coins.Aggregate(new AssetMoney(assetIdObj, 0), (accum, coin) => accum + coin.Amount);

                generateCnt = Math.Min(generateCnt, (int)(balance.Quantity / outputSize.Quantity));
                if (generateCnt == 0)
                {
                    return;
                }

                await GenerateOutputs(generateCnt, coins, hotWallet, outputSize, asset, setting);
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _logger));
        }
コード例 #11
0
        public async Task <IAsset> GetBaseAssetForClient(string clientId, bool isIosDevice)
        {
            var assetsForClient  = (await GetAssetsForClient(clientId, isIosDevice)).Where(x => x.IsBase);
            var exchangeSettings =
                await _exchangeSettingsRepository.GetOrDefaultAsync(clientId);

            var baseAsset = exchangeSettings.BaseAsset(isIosDevice);

            if (string.IsNullOrEmpty(baseAsset))
            {
                baseAsset = assetsForClient.GetFirstAssetId();
            }

            return(await _assetsDict.GetItemAsync(baseAsset));
        }
コード例 #12
0
        public async Task <CreateTransactionResponse> GetTransferFromSegwitWallet(BitcoinAddress source, Guid transactionId)
        {
            var assetSetting = await _assetSettingCache.GetItemAsync("BTC");

            var asset = await _assetRepository.GetItemAsync("BTC");

            var hotWallet = OpenAssetsHelper.ParseAddress(!string.IsNullOrEmpty(assetSetting.ChangeWallet) ? assetSetting.ChangeWallet : assetSetting.HotWallet);
            var context   = _transactionBuildContextFactory.Create(_connectionParams.Network);
            var lowVolume = new Money((decimal)asset.LowVolumeAmount, MoneyUnit.BTC);

            return(await context.Build(async() =>
            {
                var outputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(source.ToString())).OfType <Coin>()
                              .Where(o => o.Amount >= lowVolume).ToList();
                if (!outputs.Any())
                {
                    throw new BackendException($"Address {source} has not unspent outputs", ErrorCode.NoCoinsFound);
                }

                var totalAmount = new Money(outputs.Sum(o => o.Amount));

                var builder = new TransactionBuilder();
                builder.DustPrevention = false;
                builder.AddCoins(outputs);
                builder.Send(hotWallet, totalAmount);
                builder.SubtractFees();

                builder.SendEstimatedFees(await _feeProvider.GetFeeRate());

                builder.SetChange(hotWallet);

                var tx = builder.BuildTransaction(true);

                _transactionBuildHelper.AggregateOutputs(tx);

                if (tx.Outputs[0].Value <= tx.Outputs[0].GetDustThreshold(builder.StandardTransactionPolicy.MinRelayTxFee))
                {
                    throw new BackendException($"Address {source} balance is too low", ErrorCode.NoCoinsFound);
                }

                await _spentOutputService.SaveSpentOutputs(transactionId, tx);

                await SaveNewOutputs(transactionId, tx, context);

                return new CreateTransactionResponse(tx.ToHex(), transactionId);
            }));
        }
コード例 #13
0
        public async Task <IActionResult> Cashout([FromBody] CashoutRequest model)
        {
            model.DestinationAddress = model.DestinationAddress.Trim('\n', ' ', '\t');

            if (model.Amount <= 0)
            {
                throw new BackendException("Amount can't be less or equal to zero", ErrorCode.BadInputParameter);
            }

            await ValidateAddress(model.DestinationAddress, false);

            var asset = await _assetRepository.GetItemAsync(model.Asset);

            if (asset == null)
            {
                throw new BackendException("Provided asset is missing in database", ErrorCode.AssetNotFound);
            }

            var transactionId = await _builder.AddTransactionId(model.TransactionId, $"Cashout: {model.ToJson()}");

            if (OpenAssetsHelper.IsBitcoin(model.Asset))
            {
                await _cashoutRequestRepository.CreateCashoutRequest(transactionId, model.Amount, model.DestinationAddress);
            }
            else
            {
                var assetSetting = await _assetSettingCache.GetItemAsync(asset.Id);

                var hotWallet = !string.IsNullOrEmpty(assetSetting.ChangeWallet)
                    ? assetSetting.ChangeWallet
                    : assetSetting.HotWallet;

                await _transactionQueueWriter.AddCommand(transactionId, TransactionCommandType.Transfer, new TransferCommand
                {
                    Amount             = model.Amount,
                    SourceAddress      = hotWallet,
                    Asset              = model.Asset,
                    DestinationAddress = model.DestinationAddress
                }.ToJson());
            }
            return(Ok(new TransactionIdResponse
            {
                TransactionId = transactionId
            }));
        }
コード例 #14
0
        public async Task <bool> IsKycNeeded(string clientId, string assetId)
        {
            if (string.IsNullOrEmpty(assetId))
            {
                throw new ArgumentException(nameof(assetId));
            }

            var asset = await _assets.GetItemAsync(assetId);

            if (asset == null)
            {
                throw new ArgumentException(nameof(assetId));
            }

            var userKycStatus = await _kycRepository.GetKycStatusAsync(clientId);

            return(asset.KycNeeded && userKycStatus != KycStatus.Ok);
        }
コード例 #15
0
        public async Task <bool> IsKycNeeded(string clientId, string assetId)
        {
            if (string.IsNullOrEmpty(assetId))
            {
                throw new ArgumentException(nameof(assetId));
            }

            var asset = await _assets.GetItemAsync(assetId);

            if (asset == null)
            {
                throw new ArgumentException(nameof(assetId));
            }

            var userKycStatus = await _kycStatusService.GetKycStatusAsync(clientId);

            return(asset.KycNeeded && !userKycStatus.IsKycOkOrReviewDone());
        }
コード例 #16
0
        public async Task <CommandHandlingResult> Handle(StartCashoutCommand command,
                                                         IEventPublisher eventPublisher)
        {
            var address = command.Address.Trim('\n', ' ', '\t');

            try
            {
                var transactionId = await _builder.AddTransactionId(command.Id, $"Cashout: {command.ToJson()}");

                if (OpenAssetsHelper.IsBitcoin(command.AssetId))
                {
                    await _cashoutRequestRepository.CreateCashoutRequest(transactionId, command.Amount, address);
                }
                else
                {
                    var assetSetting = await _assetSettingCache.GetItemAsync(command.AssetId);

                    var hotWallet = !string.IsNullOrEmpty(assetSetting.ChangeWallet)
                        ? assetSetting.ChangeWallet
                        : assetSetting.HotWallet;

                    await _transactionQueueWriter.AddCommand(transactionId, TransactionCommandType.Transfer, new TransferCommand
                    {
                        Amount             = command.Amount,
                        SourceAddress      = hotWallet,
                        Asset              = command.AssetId,
                        DestinationAddress = command.Address
                    }.ToJson());
                }
            }
            catch (BackendException ex) when(ex.Code == ErrorCode.DuplicateTransactionId)
            {
                _logger.WriteWarning(nameof(CashinCommandHandler), nameof(Handle), $"Duplicated id: {command.Id}");
            }

            return(CommandHandlingResult.Ok());
        }
コード例 #17
0
        public async Task <IActionResult> Get(string assetPairId = null)
        {
            assetPairId = string.IsNullOrEmpty(assetPairId)
                ? null
                : assetPairId.ToUpper();

            AssetPair pair = null;

            if (!string.IsNullOrEmpty(assetPairId))
            {
                pair = await _assetPairs.GetItemAsync(assetPairId);
            }

            if (!string.IsNullOrEmpty(assetPairId) && pair == null)
            {
                return(NotFound($"Asset pair {assetPairId} not found"));
            }

            IEnumerable <IOrderBook> result = string.IsNullOrEmpty(assetPairId)
                ? await _orderBooksService.GetAllAsync()
                : await _orderBooksService.GetAsync(assetPairId);

            return(Ok(result.ToApiModel()));
        }
コード例 #18
0
        public Task <CreateMultiCashoutTransactionResult> GetMultipleCashoutTransaction(List <ICashoutRequest> cashoutRequests, Guid transactionId)
        {
            return(Retry.Try(async() =>
            {
                var context = _transactionBuildContextFactory.Create(_connectionParams.Network);
                var assetSetting = await _assetSettingCache.GetItemAsync("BTC");

                var hotWallet = OpenAssetsHelper.ParseAddress(assetSetting.HotWallet);
                var changeWallet = OpenAssetsHelper.ParseAddress(string.IsNullOrWhiteSpace(assetSetting.ChangeWallet)
                    ? assetSetting.HotWallet
                    : assetSetting.ChangeWallet);

                return await context.Build(async() =>
                {
                    var builder = new TransactionBuilder();

                    var hotWalletOutputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(hotWallet.ToString())).OfType <Coin>().ToList();

                    var hotWalletBalance = new Money(hotWalletOutputs.Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.Satoshi ?? 0));

                    var maxFeeForTransaction = Money.FromUnit(0.099M, MoneyUnit.BTC);

                    var selectedCoins = new List <Coin>();

                    var maxInputsCount = maxFeeForTransaction.Satoshi / (await _feeProvider.GetFeeRate()).GetFee(Constants.InputSize).Satoshi;
                    var tryCount = 100;
                    do
                    {
                        if (selectedCoins.Count > maxInputsCount && cashoutRequests.Count > 1)
                        {
                            cashoutRequests = cashoutRequests.Take(cashoutRequests.Count - 1).ToList();
                            selectedCoins.Clear();
                        }
                        else
                        if (selectedCoins.Count > 0)
                        {
                            break;
                        }

                        var totalAmount = Money.FromUnit(cashoutRequests.Select(o => o.Amount).Sum(), MoneyUnit.BTC);

                        if (hotWalletBalance < totalAmount + maxFeeForTransaction)
                        {
                            var changeBalance = Money.Zero;
                            List <Coin> changeWalletOutputs = new List <Coin>();
                            if (hotWallet != changeWallet)
                            {
                                changeWalletOutputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(changeWallet.ToString()))
                                                      .OfType <Coin>().ToList();
                                changeBalance = new Money(changeWalletOutputs.Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.Satoshi ?? 0));
                            }
                            if (changeBalance + hotWalletBalance >= totalAmount + maxFeeForTransaction)
                            {
                                selectedCoins.AddRange(hotWalletOutputs);
                                selectedCoins.AddRange(OpenAssetsHelper
                                                       .CoinSelect(changeWalletOutputs, totalAmount + maxFeeForTransaction - hotWalletBalance).OfType <Coin>());
                            }
                            else
                            {
                                selectedCoins.AddRange(hotWalletOutputs);
                                selectedCoins.AddRange(changeWalletOutputs);

                                int cashoutsUsedCount = 0;
                                var cashoutsAmount = await _transactionBuildHelper.CalcFee(selectedCoins.Count, cashoutRequests.Count + 1);
                                foreach (var cashoutRequest in cashoutRequests)
                                {
                                    cashoutsAmount += Money.FromUnit(cashoutRequest.Amount, MoneyUnit.BTC);
                                    if (cashoutsAmount > hotWalletBalance + changeBalance)
                                    {
                                        break;
                                    }
                                    cashoutsUsedCount++;
                                }
                                if (cashoutsUsedCount == 0)
                                {
                                    throw new BackendException("Not enough bitcoin available", ErrorCode.NotEnoughBitcoinAvailable);
                                }
                                cashoutRequests = cashoutRequests.Take(cashoutsUsedCount).ToList();
                            }

                            if (changeWallet != hotWallet)
                            {
                                if (assetSetting.Asset == Constants.DefaultAssetSetting)
                                {
                                    assetSetting = await CreateAssetSetting("BTC", assetSetting);
                                }

                                if (assetSetting.Asset != Constants.DefaultAssetSetting)
                                {
                                    await _assetSettingRepository.UpdateHotWallet(assetSetting.Asset, assetSetting.ChangeWallet);
                                }
                            }
                        }
                        else
                        {
                            selectedCoins.AddRange(OpenAssetsHelper.CoinSelect(hotWalletOutputs, totalAmount + maxFeeForTransaction).OfType <Coin>());
                        }
                    } while (tryCount-- > 0);

                    var selectedCoinsAmount = new Money(selectedCoins.Sum(o => o.Amount));
                    var sendAmount = new Money(cashoutRequests.Sum(o => o.Amount), MoneyUnit.BTC);

                    builder.AddCoins(selectedCoins);
                    foreach (var cashout in cashoutRequests)
                    {
                        var amount = Money.FromUnit(cashout.Amount, MoneyUnit.BTC);
                        builder.Send(OpenAssetsHelper.ParseAddress(cashout.DestinationAddress), amount);
                    }

                    builder.Send(changeWallet, selectedCoinsAmount - sendAmount);
                    builder.SubtractFees();
                    builder.SendEstimatedFees(await _feeProvider.GetFeeRate());
                    builder.SetChange(changeWallet);

                    var tx = builder.BuildTransaction(true);
                    _transactionBuildHelper.AggregateOutputs(tx);

                    await _broadcastedOutputRepository.InsertOutputs(
                        tx.Outputs.AsCoins().Where(o => o.ScriptPubKey == changeWallet.ScriptPubKey).Select(o =>
                                                                                                            new BroadcastedOutput(o, transactionId, _connectionParams.Network)).ToList());

                    await _spentOutputService.SaveSpentOutputs(transactionId, tx);

                    return new CreateMultiCashoutTransactionResult
                    {
                        Transaction = tx,
                        UsedRequests = cashoutRequests
                    };
                });
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log));
        }
コード例 #19
0
 public Task <AssetPair> GetAssetPairAsync(string assetPairId)
 {
     return(_assetPairsCache.GetItemAsync(assetPairId));
 }
コード例 #20
0
 public Task <Asset> GetAssetAsync(string assetId)
 {
     return(_assetsCache.GetItemAsync(assetId));
 }
コード例 #21
0
 public async Task <IAssetDefinition> GetAssetAsync(string assetId)
 {
     return(await _assetDefinitionCachedDictionary.GetItemAsync(assetId));
 }