public IEnumerable <ICoin> Select(IEnumerable <ICoin> coins, IMoney target) { var selector = new DefaultCoinSelector(); var selectedCoins = selector.Select(coins, target).ToList(); if (!selectedCoins.Any(o => o.Outpoint == FromOutpoint)) { var fromCoin = coins.Single(o => o.Outpoint == this.FromOutpoint); var fromCoinAmount = fromCoin.Amount as Money; var selectedAmount = selectedCoins.Select(o => o.Amount).Cast <Money>().Sum(); if (fromCoinAmount >= selectedAmount) { selectedCoins = new List <ICoin> { fromCoin } } ; else { selectedCoins.Insert(0, fromCoin); } } return(selectedCoins); }
public async Task <Money> CalculateFees(Account account, BitcoinAddress receiver, Money amount, int blocks) { try { var rate = await EstimateSmartFee(blocks); _logger.LogCritical($"Estimated fee rate: {rate}"); ExtKey key = _masterKey.Derive(new KeyPath($"m/84'/0'/{account.Derivation}'/0/0")); var sender = key.PrivateKey.PubKey.GetSegwitAddress(Network.Main); var unspentCoins = await _client.ListUnspentAsync(6, int.MaxValue, sender); var coins = unspentCoins.Select(c => c.AsCoin()).ToArray(); if (coins.Select(c => c.Amount).Sum() < amount) { _logger.LogCritical("User does not have enough funds."); return(null); } var coinSelector = new DefaultCoinSelector { GroupByScriptPubKey = false }; var builder = Network.Main.CreateTransactionBuilder(); builder.DustPrevention = false; var tx = builder .SetCoinSelector(new DefaultCoinSelector { GroupByScriptPubKey = false }) .AddCoins(coins) .AddKeys(key.PrivateKey) .Send(receiver, amount) .SetChange(sender) .SendEstimatedFees(rate) .BuildTransaction(true); var fees = rate.GetFee(tx); _logger.LogCritical($"Transaction Fees: {fees}"); _logger.LogCritical($"TransactionCheckResult: {tx.Check()}"); if (tx.Check() == TransactionCheckResult.Success) { return(fees); } else { return(null); } } catch (Exception e) { _logger.LogError(e.ToString()); } return(null); }
public void CanSelectCoin() { var selector = new DefaultCoinSelector(0); Assert.Null(selector.Select(new ICoin[] { CreateCoin("9") }, Money.Parse("10.0"))); Assert.NotNull(selector.Select(new ICoin[] { CreateCoin("9"), CreateCoin("1") }, Money.Parse("10.0"))); Assert.NotNull(selector.Select(new ICoin[] { CreateCoin("10.0") }, Money.Parse("10.0"))); Assert.NotNull(selector.Select(new ICoin[] { CreateCoin("5.0"), CreateCoin("4.0"), CreateCoin("11.0"), }, Money.Parse("10.0"))); Assert.NotNull(selector.Select(new ICoin[] { CreateCoin("3.0"), CreateCoin("3.0"), CreateCoin("3.0"), CreateCoin("3.0"), CreateCoin("3.0") }, Money.Parse("10.0"))); }
/// <summary> /// Checks whether we can send the specified amount <paramref name="amount"/> of bitcoin to /// the wallet of the user with the Id <paramref name="id"/> parameter. /// </summary> /// <param name="id">Id of the receiver.</param> /// <param name="money">Amount of bitcoin to send.</param> public async Task <bool> BitarCanMakePayment(int id, Money amount) { try { var account = await GetAccount(id); ExtKey bitarKey = _masterKey.Derive(new KeyPath($"m/84'/0'/0'/0/0")); var sender = bitarKey.PrivateKey.PubKey.GetSegwitAddress(Network.Main); ExtKey key = _masterKey.Derive(new KeyPath($"m/84'/0'/0'/0/0")); var receiver = key.PrivateKey.PubKey.GetSegwitAddress(Network.Main); var unspentCoins = await _client.ListUnspentAsync(0, int.MaxValue, sender); if (unspentCoins == null) { return(false); } foreach (var coin in unspentCoins) { _logger.LogCritical( $"Address: {coin.Address}\n" + $"Amount: {coin.Amount}\n" + $"Confirmations: {coin.Confirmations}\n" + $"OutPoint: {coin.OutPoint}"); } var coins = unspentCoins.Select(c => c.AsCoin()).ToArray(); if (coins.Select(c => c.Amount).Sum() < amount) { _logger.LogCritical("Not enough funds."); _logger.LogCritical($"Sum: {coins.Select(c => c.Amount).Sum()}"); _logger.LogCritical($"Amount: {amount}"); _logger.LogCritical("==Coins=="); _logger.LogCritical($"{coins.Select(c => c.Amount)}"); _logger.LogCritical($"======"); _logger.LogCritical("==Coins2=="); _logger.LogCritical($"{coins.Count()}"); _logger.LogCritical($"======"); return(false); } var rate = await _client.EstimateSmartFeeAsync(25, EstimateSmartFeeMode.Economical); _logger.LogCritical($"Estimated fee rate: {rate.FeeRate}"); var coinSelector = new DefaultCoinSelector { GroupByScriptPubKey = false }; var builder = Network.Main.CreateTransactionBuilder(); builder.DustPrevention = false; var tx = builder .SetCoinSelector(new DefaultCoinSelector { GroupByScriptPubKey = false }) .AddCoins(coins) .AddKeys(bitarKey.PrivateKey) .Send(receiver, amount) .SetChange(sender) .SendEstimatedFees(rate.FeeRate) .BuildTransaction(true); _logger.LogCritical( $"vsize: {tx.GetVirtualSize()}\n" + $"{sender} sending {amount} btc to {receiver}" + $"with {builder.EstimateFees(tx, rate.FeeRate)} fees\n" + $"{tx.ToString()}"); _logger.LogCritical($"TransactionCheckResult: {tx.Check()}"); if (tx.Check() == TransactionCheckResult.Success) { var txId = tx.GetHash(); _logger.LogCritical($"TxId: {txId}"); return(true); } else { return(false); } } catch (Exception e) { _logger.LogError(e.ToString()); } return(false); }
/// <summary> /// Sends specified amount of bitcoin to an address. /// Change address is the same as the sender address. /// </summary> /// <param name="accountId">AccountId of the sender.</param> /// <param name="receiver">Bitcoin address to send the money to.</param> /// <param name="money">Amount of bitcoin to send.</param> /// <param name="fees">Amount of fees to be included in the transaction.</param> /// <remarks> /// Change address is the same as the sender address. /// </remarks> public async Task <uint256> SendBitcoin(int accountId, BitcoinAddress receiver, Money amount, int blocks) { try { var account = await GetAccount(accountId); var rate = await _client.EstimateSmartFeeAsync(blocks, EstimateSmartFeeMode.Economical); _logger.LogCritical($"Estimated fee rate: {rate.FeeRate}"); ExtKey key = _masterKey.Derive(new KeyPath($"m/84'/0'/{account.Derivation}'/0/0")); var sender = key.PrivateKey.PubKey.GetSegwitAddress(Network.Main); UnspentCoin[] unspentCoins; unspentCoins = await _client.ListUnspentAsync(6, int.MaxValue, sender); if (unspentCoins == null) { return(null); } foreach (var coin in unspentCoins) { _logger.LogCritical( $"Address: {coin.Address}\n" + $"Amount: {coin.Amount}\n" + $"Confirmations: {coin.Confirmations}\n" + $"OutPoint: {coin.OutPoint}"); } var coins = unspentCoins.Select(c => c.AsCoin()).ToArray(); if (coins.Select(c => c.Amount).Sum() < amount) { _logger.LogCritical("User does not have enough funds."); return(null); } var coinSelector = new DefaultCoinSelector { GroupByScriptPubKey = false }; var builder = Network.Main.CreateTransactionBuilder(); builder.DustPrevention = false; var tx = builder .SetCoinSelector(new DefaultCoinSelector { GroupByScriptPubKey = false }) .AddCoins(coins) .AddKeys(key.PrivateKey) .Send(receiver, amount) .SetChange(sender) .SendEstimatedFees(rate.FeeRate) .BuildTransaction(true); var fees = rate.FeeRate.GetFee(tx); _logger.LogCritical( $"vsize: {tx.GetVirtualSize()}\n" + $"{sender} sending {amount} btc to {receiver}" + $"with {fees} fees\n" + $"{tx.ToString()}"); _logger.LogCritical($"TransactionCheckResult: {tx.Check()}"); if (tx.Check() == TransactionCheckResult.Success) { var txId = tx.GetHash(); _logger.LogCritical($"TxId: {txId}"); // return txId; // Temporary -- Only for testing. return(await _client.SendRawTransactionAsync(tx)); } else { return(null); } } catch (Exception e) { _logger.LogError(e.ToString()); } return(null); }