public async Task <IBuiltTransaction> GetManyOutputsTransferTransactionAsync(OperationInput fromAddress, IList <OperationOutput> toAddresses, bool includeFee) { var input = fromAddress.ToBitcoinInput(_addressValidator); var outputs = toAddresses.Select(o => o.ToBitcoinOutput(_addressValidator)).ToList(); var builder = new TransactionBuilder(); foreach (var operationBitcoinOutput in outputs) { builder.Send(operationBitcoinOutput.Address, operationBitcoinOutput.Amount); } var minConfirmations = includeFee ? _confirmationsSettings.MinConfirmationsToDetectOperation : 0; var coins = await GetUnspentCoins(input, minConfirmations); var addressBalance = coins.Sum(o => o.Amount); var sendAmount = outputs.Sum(o => o.Amount);; var sentFees = Money.Zero; builder.SetChange(input.Address); var change = addressBalance - sendAmount; if (change < new TxOut(Money.Zero, input.Address).GetDustThreshold(builder.StandardTransactionPolicy.MinRelayTxFee) && change > 0) { builder.SendFees(change); sentFees = change; } builder.AddCoins(coins); var calculatedFee = await _feeService.CalcFeeForTransactionAsync(builder) - sentFees; var requiredBalance = sendAmount + calculatedFee; if (addressBalance < requiredBalance) { throw new BusinessException($"The sum of total applicable outputs is less than the required : {requiredBalance} satoshis.", ErrorCode.NotEnoughFundsAvailable); } if (calculatedFee > 0) { builder.SendFees(calculatedFee); } var tx = builder.BuildTransaction(false); var usedCoins = builder.FindSpentCoins(tx).ToArray(); return(BuiltTransaction.Create(tx, tx.GetFee(usedCoins), usedCoins.Cast <Coin>())); }
public async Task <IBuiltTransaction> GetManyInputsTransferTransactionAsync(IList <OperationInput> fromAddresses, OperationOutput toAddress) { var inputs = fromAddresses.Select(o => o.ToBitcoinInput(_addressValidator)).ToList(); var output = toAddress.ToBitcoinOutput(_addressValidator); var builder = new TransactionBuilder(); var totalBalance = Money.Zero; var sendAmount = inputs.Sum(o => o.Amount); var sentFees = Money.Zero; foreach (var operationBitcoinInput in inputs) { var coins = await GetUnspentCoins(operationBitcoinInput); builder.AddCoins(coins); var addressBalance = coins.Sum(o => o.Amount); if (addressBalance < operationBitcoinInput.Amount) { throw new BusinessException($"The sum of total applicable outputs is less than the required : {operationBitcoinInput.Amount.Satoshi} satoshis.", ErrorCode.NotEnoughFundsAvailable); } // send change to source address var change = addressBalance - operationBitcoinInput.Amount; if (change < new TxOut(Money.Zero, operationBitcoinInput.Address).GetDustThreshold(builder.StandardTransactionPolicy.MinRelayTxFee)) { builder.SendFees(change); sentFees += change; } else { builder.Send(operationBitcoinInput.Address, change); } totalBalance += addressBalance; } builder.Send(output.Address, sendAmount); var calculatedFee = await _feeService.CalcFeeForTransactionAsync(builder) - sentFees; var requiredBalance = sendAmount; if (totalBalance < requiredBalance) { throw new BusinessException( $"The sum of total applicable outputs is less than the required : {requiredBalance} satoshis.", ErrorCode.NotEnoughFundsAvailable); } if (calculatedFee > sendAmount) { throw new BusinessException( $"The sum of total applicable outputs is less than the required fee:{calculatedFee} satoshis.", ErrorCode.BalanceIsLessThanFee); } if (calculatedFee > 0) { builder.SubtractFees(); builder.SendFees(calculatedFee); } var tx = builder.BuildTransaction(false); var usedCoins = builder.FindSpentCoins(tx).ToArray(); return(BuiltTransaction.Create(tx, tx.GetFee(usedCoins), usedCoins.Cast <Coin>())); }
public async Task <IBuiltTransaction> GetTransferTransactionAsync(OperationInput fromAddress, OperationOutput toAddress, bool includeFee) { var input = fromAddress.ToBitcoinInput(_addressValidator); var output = toAddress.ToBitcoinOutput(_addressValidator); var builder = new TransactionBuilder(); var coins = await GetUnspentCoins(input); builder.AddCoins(coins); var addressBalance = coins.Sum(o => o.Amount); if (addressBalance < input.Amount) { throw new BusinessException($"The sum of total applicable outputs is less than the required : {input.Amount.Satoshi} satoshis.", ErrorCode.NotEnoughFundsAvailable); } var sentFees = Money.Zero; var change = addressBalance - input.Amount; if (change < new TxOut(Money.Zero, input.Address).GetDustThreshold(builder.StandardTransactionPolicy.MinRelayTxFee).Satoshi) { builder.SendFees(change); sentFees = change; } builder.Send(output.Address, input.Amount) .SetChange(input.Address); var calculatedFee = await _feeService.CalcFeeForTransactionAsync(builder) - sentFees; var requiredBalance = input.Amount + (includeFee ? Money.Zero : calculatedFee); if (addressBalance < requiredBalance) { throw new BusinessException( $"The sum of total applicable outputs is less than the required : {requiredBalance} satoshis.", ErrorCode.NotEnoughFundsAvailable); } if (includeFee) { if (calculatedFee > input.Amount) { throw new BusinessException( $"The sum of total applicable outputs is less than the required fee:{calculatedFee} satoshis.", ErrorCode.BalanceIsLessThanFee); } builder.SubtractFees(); } if (calculatedFee > 0) { builder.SendFees(calculatedFee); } var tx = builder.BuildTransaction(false); var usedCoins = builder.FindSpentCoins(tx).ToArray(); return(BuiltTransaction.Create(tx, tx.GetFee(usedCoins), usedCoins.Cast <Coin>())); }