public static async IAsyncEnumerable <ChangeAvoidanceSuggestionViewModel> GenerateSuggestionsAsync(
        TransactionInfo transactionInfo,
        BitcoinAddress destination,
        Wallet wallet,
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        var selections = ChangelessTransactionCoinSelector.GetAllStrategyResultsAsync(
            transactionInfo.Coins,
            transactionInfo.FeeRate,
            new TxOut(transactionInfo.Amount, destination),
            cancellationToken).ConfigureAwait(false);

        await foreach (var selection in selections)
        {
            if (selection.Any())
            {
                BuildTransactionResult transaction = TransactionHelpers.BuildChangelessTransaction(
                    wallet,
                    destination,
                    transactionInfo.UserLabels,
                    transactionInfo.FeeRate,
                    selection,
                    tryToSign: false);

                yield return(new ChangeAvoidanceSuggestionViewModel(
                                 transactionInfo.Amount.ToDecimal(MoneyUnit.BTC),
                                 transaction,
                                 wallet.Synchronizer.UsdExchangeRate));
            }
        }
    }
Example #2
0
    public static async IAsyncEnumerable <ChangeAvoidanceSuggestionViewModel> GenerateSuggestionsAsync(
        TransactionInfo transactionInfo, BitcoinAddress destination, Wallet wallet, [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        Task <ChangeAvoidanceSuggestionViewModel?> bnbSuggestionTask = Task.Run(() =>
        {
            if (ChangelessTransactionCoinSelector.TryGetCoins(transactionInfo.Coins, transactionInfo.FeeRate, new TxOut(transactionInfo.Amount, destination), out IEnumerable <SmartCoin>?selection, cancellationToken))
            {
                BuildTransactionResult transaction = TransactionHelpers.BuildChangelessTransaction(
                    wallet,
                    destination,
                    transactionInfo.UserLabels,
                    transactionInfo.FeeRate,
                    selection,
                    tryToSign: false);

                return(new ChangeAvoidanceSuggestionViewModel(
                           transactionInfo.Amount.ToDecimal(MoneyUnit.BTC),
                           transaction,
                           wallet.Synchronizer.UsdExchangeRate,
                           isOriginal: false));
            }

            return(null);
        });

        ChangeAvoidanceSuggestionViewModel?bnbSuggestion = await bnbSuggestionTask;

        if (bnbSuggestion is not null)
        {
            yield return(bnbSuggestion);
        }
    }
Example #3
0
    public void GoodSuggestion()
    {
        using Key key = new();

        List <SmartCoin> coins = GenerateDummySmartCoins(key, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288);
        var target             = Money.Satoshis(150_000);

        var txOut = new TxOut(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));

        bool found = ChangelessTransactionCoinSelector.TryGetCoins(coins, new FeeRate(satoshiPerByte: 4), txOut, out IEnumerable <SmartCoin>?selectedCoins);

        Assert.True(found);

        long[] solution = selectedCoins !.Select(x => x.Amount.Satoshi).ToArray();
        Assert.Equal(new long[] { 100_000, 50_000, 6_025 }, solution);
Example #4
0
    public static async IAsyncEnumerable <ChangeAvoidanceSuggestionViewModel> GenerateSuggestionsAsync(
        TransactionInfo transactionInfo,
        BitcoinAddress destination,
        Wallet wallet,
        ImmutableArray <SmartCoin> coinsToUse,
        int maxInputCount,
        decimal usdExchangeRate,
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        var selections = ChangelessTransactionCoinSelector.GetAllStrategyResultsAsync(
            coinsToUse,
            transactionInfo.FeeRate,
            new TxOut(transactionInfo.Amount, destination),
            maxInputCount,
            cancellationToken).ConfigureAwait(false);

        HashSet <Money> foundSolutionsByAmount = new();

        await foreach (var selection in selections)
        {
            if (selection.Any())
            {
                BuildTransactionResult transaction = TransactionHelpers.BuildChangelessTransaction(
                    wallet,
                    destination,
                    transactionInfo.UserLabels,
                    transactionInfo.FeeRate,
                    selection,
                    tryToSign: false);

                var destinationAmount = transaction.CalculateDestinationAmount();

                // If BnB solutions become the same transaction somehow, do not show the same suggestion twice.
                if (!foundSolutionsByAmount.Contains(destinationAmount))
                {
                    foundSolutionsByAmount.Add(destinationAmount);

                    yield return(new ChangeAvoidanceSuggestionViewModel(
                                     transactionInfo.Amount.ToDecimal(MoneyUnit.BTC),
                                     transaction,
                                     usdExchangeRate));
                }
            }
        }
    }
    public void GoodSuggestion()
    {
        using Key key = new();

        List <SmartCoin> coins = GenerateDummySmartCoins(key, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288);
        var target             = Money.Satoshis(150_000);
        var feeRate            = new FeeRate(Money.Satoshis(2));
        var txOut = new TxOut(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));

        long[] inputCosts = coins.Select(x => feeRate.GetFee(x.ScriptPubKey.EstimateInputVsize()).Satoshi).ToArray();

        Dictionary <SmartCoin, long> inputEffectiveValues = new(coins.ToDictionary(x => x, x => x.EffectiveValue(feeRate).Satoshi));
        var strategy = new MoreSelectionStrategy(target, inputEffectiveValues.Values.ToArray(), inputCosts);

        bool found = ChangelessTransactionCoinSelector.TryGetCoins(strategy, target, inputEffectiveValues, out var selectedCoins);

        Assert.True(found);

        long[] solution = selectedCoins !.Select(x => x.Amount.Satoshi).ToArray();
        Assert.Equal(new long[] { 100_000, 50_000, 6_025 }, solution);