Example #1
0
        public async Task Transfer(BitcoinAddress from, BitcoinAddress to, decimal amount)
        {
            var amountMoney = Money.FromUnit(amount, MoneyUnit.BTC);
            var coins       = (await _bccOutputService.GetUnspentOutputs(from.ToString())).OfType <Coin>().Cast <ICoin>().ToList();

            var availableAmount = coins.Select(o => o.Amount).DefaultIfEmpty().Select(o => (Money)o ?? Money.Zero).Sum();

            await _log.WriteInfoAsync(nameof(BccTransactionService), nameof(Transfer), null,
                                      $"Available amount of {from} - {availableAmount} satoshis");

            var builder = new TransactionBuilder();
            var context = new TransactionBuildContext(_connectionParams.Network, null, null);
            await _transactionBuildHelper.SendWithChange(builder, context, coins, to, amountMoney, from, false);

            var trId = Guid.NewGuid();

            var transaction = builder.BuildTransaction(true);

            var fee = await _transactionBuildHelper.CalcFee(transaction);

            var output = transaction.Outputs.First(o => o.ScriptPubKey.GetDestinationAddress(_connectionParams.Network) == to);

            output.Value -= fee;

            var signedTr = await _signatureApi.SignBccTransaction(transaction.ToHex());

            await Broadcast(signedTr, trId);
        }
Example #2
0
        public Task <CreateTransactionResponse> GetMultipleTransferTransaction(BitcoinAddress destination, IAsset asset, Dictionary <string, decimal> transferAddresses, int feeRate, decimal fixedFee, Guid transactionId)
        {
            return(Retry.Try(async() =>
            {
                var context = _transactionBuildContextFactory.Create(_connectionParams.Network);

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

                    builder.SetChange(destination, ChangeType.Uncolored);

                    foreach (var transferAddress in transferAddresses)
                    {
                        var source = OpenAssetsHelper.ParseAddress(transferAddress.Key);
                        await TransferOneDirection(builder, context, source, transferAddress.Value, asset, destination);
                    }

                    Transaction buildedTransaction;
                    try
                    {
                        buildedTransaction = builder.BuildTransaction(true);
                    }
                    catch (NotEnoughFundsException ex)
                    {
                        if (ex.Missing is Money)
                        {
                            var missingAmount = ((Money)ex.Missing).Satoshi;
                            _transactionBuildHelper.AddFakeInput(builder, new Money(missingAmount, MoneyUnit.Satoshi));
                            buildedTransaction = builder.BuildTransaction(true);
                        }
                        else
                        {
                            throw;
                        }
                    }

                    _transactionBuildHelper.RemoveFakeInput(buildedTransaction);

                    _transactionBuildHelper.AggregateOutputs(buildedTransaction);

                    var fee = fixedFee > 0 ? Money.FromUnit(fixedFee, MoneyUnit.BTC) : await _transactionBuildHelper.CalcFee(buildedTransaction, feeRate);

                    foreach (var output in buildedTransaction.Outputs)
                    {
                        if (output.ScriptPubKey.GetDestinationAddress(_connectionParams.Network) == destination)
                        {
                            if (output.Value <= fee)
                            {
                                throw new BackendException("Amount is lower than fee", ErrorCode.NotEnoughBitcoinAvailable);
                            }
                            output.Value -= fee;
                            break;
                        }
                    }

                    await _spentOutputService.SaveSpentOutputs(transactionId, buildedTransaction);

                    await SaveNewOutputs(transactionId, buildedTransaction, context);

                    return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId);
                });
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log));
        }