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