MetacoSA / NBitcoin

Comprehensive Bitcoin library for the .NET framework.
MIT License
1.88k stars 848 forks source link

Estimating Fee from transactionbuilder that add coins incrementally to limit maximum payed by all #1018

Open talespa opened 3 years ago

talespa commented 3 years ago

@NicolasDorier My goals is sweep "scrap" a wallet but limiting the maximum fee i want to pay putting only few inputs as necessary, if this limit are reach based on transaction size and feerate i build and send them. The fee need to be payed by all coins added but i want to add coins individualy one by one and estimate fee based on this composition befor add next, the problem is when i add a new coin on transactionbuilder with bellow code the builder will subtract fee only from last entry currentgroup.builders added by Send(...) method.

var builder = _network.CreateTransactionBuilder()
builder.SetChange(destination);

foreach(var coin in spendableCoins)
{
    builder.AddCoin(coin );
    var unsignedTx = builder.Send(destination, coin.amount)
        .SubtractFees()
        .SendEstimatedFees(feeRate);
    var fee2Pay = builder.EstimateFees(feeRate);
    if ( fee2Pay > feeLimit) break;
}

My hack was to include a method in NBitcoin.TransactionBuilder class that clear builders before i call

public TransactionBuilder ClearBuilders()
{
    CurrentGroup.Builders.Clear();
    _LastSendBuilder = null;
    return this;
}

and call this every time i add new input coin:

foreach(var coin in spendableCoins)
{
    builder.AddCoin(coin );
    builder.ClearBuilders();
    builder.SendAll(destination)
        .SubtractFees()
        .SendEstimatedFees(feeRate);
    var fee2Pay = builder.EstimateFees(feeRate);
    if ( fee2Pay > feeLimit) break;
}

But it is hard to be maintained because i use Nuget package in deployment.

It doesn't seem to be the best solution, have you any other way to do this?

NicolasDorier commented 3 years ago

I would say just do something like

var includedCoins = new List<ICoin>();
foreach(var coin in spendableCoins)
{
        var builder = network.CreateTransactionBuilder();
    builder.AddCoin(includedCoins);
    builder.SendAll(destination)
        .SubtractFees()
        .SendEstimatedFees(feeRate);
    var fee2Pay = builder.EstimateFees(feeRate);
    if ( fee2Pay > feeLimit) break;
       includedCoins.Add(coin);
}

Then you don't need your custom method.