@@ -45,6 +45,8 @@ using interfaces::FoundBlock;
45
45
static const std::string WALLET_ENDPOINT_BASE = " /wallet/" ;
46
46
static const std::string HELP_REQUIRING_PASSPHRASE{" \n Requires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n " };
47
47
48
+ static const uint32_t WALLET_BTC_KB_TO_SAT_B = COIN / 1000 ; // 1 sat / B = 0.00001 BTC / kB
49
+
48
50
static inline bool GetAvoidReuseFlag (const CWallet* const pwallet, const UniValue& param) {
49
51
bool can_avoid_reuse = pwallet->IsWalletFlagSet (WALLET_FLAG_AVOID_REUSE);
50
52
bool avoid_reuse = param.isNull () ? can_avoid_reuse : param.get_bool ();
@@ -191,6 +193,42 @@ static std::string LabelFromValue(const UniValue& value)
191
193
return label;
192
194
}
193
195
196
+ /* *
197
+ * Update coin control with fee estimation based on the given parameters
198
+ *
199
+ * @param[in] pwallet Wallet pointer
200
+ * @param[in,out] cc Coin control which is to be updated
201
+ * @param[in] estimate_mode String value (e.g. "ECONOMICAL")
202
+ * @param[in] estimate_param Parameter (blocks to confirm, explicit fee rate, etc)
203
+ * @throws a JSONRPCError if estimate_mode is unknown, or if estimate_param is missing when required
204
+ */
205
+ static void SetFeeEstimateMode (const CWallet* pwallet, CCoinControl& cc, const UniValue& estimate_mode, const UniValue& estimate_param)
206
+ {
207
+ if (!estimate_mode.isNull ()) {
208
+ if (!FeeModeFromString (estimate_mode.get_str (), cc.m_fee_mode )) {
209
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Invalid estimate_mode parameter" );
210
+ }
211
+ }
212
+
213
+ if (cc.m_fee_mode == FeeEstimateMode::BTC_KB || cc.m_fee_mode == FeeEstimateMode::SAT_B) {
214
+ if (estimate_param.isNull ()) {
215
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Selected estimate_mode requires a fee rate" );
216
+ }
217
+
218
+ CAmount fee_rate = AmountFromValue (estimate_param);
219
+ if (cc.m_fee_mode == FeeEstimateMode::SAT_B) {
220
+ fee_rate /= WALLET_BTC_KB_TO_SAT_B;
221
+ }
222
+
223
+ cc.m_feerate = CFeeRate (fee_rate);
224
+
225
+ // default RBF to true for explicit fee rate modes
226
+ if (cc.m_signal_bip125_rbf == boost::none) cc.m_signal_bip125_rbf = true ;
227
+ } else if (!estimate_param.isNull ()) {
228
+ cc.m_confirm_target = ParseConfirmTarget (estimate_param, pwallet->chain ().estimateMaxBlocks ());
229
+ }
230
+ }
231
+
194
232
static UniValue getnewaddress (const JSONRPCRequest& request)
195
233
{
196
234
RPCHelpMan{" getnewaddress" ,
@@ -369,7 +407,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
369
407
{" subtractfeefromamount" , RPCArg::Type::BOOL, /* default */ " false" , " The fee will be deducted from the amount being sent.\n "
370
408
" The recipient will receive less bitcoins than you enter in the amount field." },
371
409
{" replaceable" , RPCArg::Type::BOOL, /* default */ " wallet default" , " Allow this transaction to be replaced by a transaction with higher fees via BIP 125" },
372
- {" conf_target" , RPCArg::Type::NUM, /* default */ " wallet default" , " Confirmation target (in blocks)" },
410
+ {" conf_target" , RPCArg::Type::NUM, /* default */ " wallet default" , " Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + " /kB or " + CURRENCY_ATOM + " /B estimate modes) " },
373
411
{" estimate_mode" , RPCArg::Type::STR, /* default */ " unset" , std::string () + " The fee estimate mode, must be one of (case insensitive):\n "
374
412
" \" " + FeeModes (" \"\n\" " ) + " \" " },
375
413
{" avoid_reuse" , RPCArg::Type::BOOL, /* default */ " true" , " (only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n "
@@ -382,6 +420,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
382
420
HelpExampleCli (" sendtoaddress" , " \" " + EXAMPLE_ADDRESS[0 ] + " \" 0.1" )
383
421
+ HelpExampleCli (" sendtoaddress" , " \" " + EXAMPLE_ADDRESS[0 ] + " \" 0.1 \" donation\" \" seans outpost\" " )
384
422
+ HelpExampleCli (" sendtoaddress" , " \" " + EXAMPLE_ADDRESS[0 ] + " \" 0.1 \"\" \"\" true" )
423
+ + HelpExampleCli (" sendtoaddress" , " \" " + EXAMPLE_ADDRESS[0 ] + " \" 0.1 \"\" \"\" false true 0.00002 " + (CURRENCY_UNIT + " /kB" ))
424
+ + HelpExampleCli (" sendtoaddress" , " \" " + EXAMPLE_ADDRESS[0 ] + " \" 0.1 \"\" \"\" false true 2 " + (CURRENCY_ATOM + " /B" ))
385
425
+ HelpExampleRpc (" sendtoaddress" , " \" " + EXAMPLE_ADDRESS[0 ] + " \" , 0.1, \" donation\" , \" seans outpost\" " )
386
426
},
387
427
}.Check (request);
@@ -423,20 +463,12 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
423
463
coin_control.m_signal_bip125_rbf = request.params [5 ].get_bool ();
424
464
}
425
465
426
- if (!request.params [6 ].isNull ()) {
427
- coin_control.m_confirm_target = ParseConfirmTarget (request.params [6 ], pwallet->chain ().estimateMaxBlocks ());
428
- }
429
-
430
- if (!request.params [7 ].isNull ()) {
431
- if (!FeeModeFromString (request.params [7 ].get_str (), coin_control.m_fee_mode )) {
432
- throw JSONRPCError (RPC_INVALID_PARAMETER, " Invalid estimate_mode parameter" );
433
- }
434
- }
435
-
436
466
coin_control.m_avoid_address_reuse = GetAvoidReuseFlag (pwallet, request.params [8 ]);
437
467
// We also enable partial spend avoidance if reuse avoidance is set.
438
468
coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse ;
439
469
470
+ SetFeeEstimateMode (pwallet, coin_control, request.params [7 ], request.params [6 ]);
471
+
440
472
EnsureWalletIsUnlocked (pwallet);
441
473
442
474
CTransactionRef tx = SendMoney (pwallet, dest, nAmount, fSubtractFeeFromAmount , coin_control, std::move (mapValue));
@@ -778,7 +810,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
778
810
},
779
811
},
780
812
{" replaceable" , RPCArg::Type::BOOL, /* default */ " wallet default" , " Allow this transaction to be replaced by a transaction with higher fees via BIP 125" },
781
- {" conf_target" , RPCArg::Type::NUM, /* default */ " wallet default" , " Confirmation target (in blocks)" },
813
+ {" conf_target" , RPCArg::Type::NUM, /* default */ " wallet default" , " Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + " /kB or " + CURRENCY_ATOM + " /B estimate modes) " },
782
814
{" estimate_mode" , RPCArg::Type::STR, /* default */ " unset" , std::string () + " The fee estimate mode, must be one of (case insensitive):\n "
783
815
" \" " + FeeModes (" \"\n\" " ) + " \" " },
784
816
},
@@ -826,15 +858,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
826
858
coin_control.m_signal_bip125_rbf = request.params [5 ].get_bool ();
827
859
}
828
860
829
- if (!request.params [6 ].isNull ()) {
830
- coin_control.m_confirm_target = ParseConfirmTarget (request.params [6 ], pwallet->chain ().estimateMaxBlocks ());
831
- }
832
-
833
- if (!request.params [7 ].isNull ()) {
834
- if (!FeeModeFromString (request.params [7 ].get_str (), coin_control.m_fee_mode )) {
835
- throw JSONRPCError (RPC_INVALID_PARAMETER, " Invalid estimate_mode parameter" );
836
- }
837
- }
861
+ SetFeeEstimateMode (pwallet, coin_control, request.params [7 ], request.params [6 ]);
838
862
839
863
std::set<CTxDestination> destinations;
840
864
std::vector<CRecipient> vecSend;
@@ -2978,6 +3002,12 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
2978
3002
2979
3003
if (options.exists (" feeRate" ))
2980
3004
{
3005
+ if (options.exists (" conf_target" )) {
3006
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Cannot specify both conf_target and feeRate" );
3007
+ }
3008
+ if (options.exists (" estimate_mode" )) {
3009
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Cannot specify both estimate_mode and feeRate" );
3010
+ }
2981
3011
coinControl.m_feerate = CFeeRate (AmountFromValue (options[" feeRate" ]));
2982
3012
coinControl.fOverrideFeeRate = true ;
2983
3013
}
@@ -2988,20 +3018,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
2988
3018
if (options.exists (" replaceable" )) {
2989
3019
coinControl.m_signal_bip125_rbf = options[" replaceable" ].get_bool ();
2990
3020
}
2991
- if (options.exists (" conf_target" )) {
2992
- if (options.exists (" feeRate" )) {
2993
- throw JSONRPCError (RPC_INVALID_PARAMETER, " Cannot specify both conf_target and feeRate" );
2994
- }
2995
- coinControl.m_confirm_target = ParseConfirmTarget (options[" conf_target" ], pwallet->chain ().estimateMaxBlocks ());
2996
- }
2997
- if (options.exists (" estimate_mode" )) {
2998
- if (options.exists (" feeRate" )) {
2999
- throw JSONRPCError (RPC_INVALID_PARAMETER, " Cannot specify both estimate_mode and feeRate" );
3000
- }
3001
- if (!FeeModeFromString (options[" estimate_mode" ].get_str (), coinControl.m_fee_mode )) {
3002
- throw JSONRPCError (RPC_INVALID_PARAMETER, " Invalid estimate_mode parameter" );
3003
- }
3004
- }
3021
+ SetFeeEstimateMode (pwallet, coinControl, options[" estimate_mode" ], options[" conf_target" ]);
3005
3022
}
3006
3023
} else {
3007
3024
// if options is null and not a bool
@@ -3068,7 +3085,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
3068
3085
},
3069
3086
{" replaceable" , RPCArg::Type::BOOL, /* default */ " wallet default" , " Marks this transaction as BIP125 replaceable.\n "
3070
3087
" Allows this transaction to be replaced by a transaction with higher fees" },
3071
- {" conf_target" , RPCArg::Type::NUM, /* default */ " wallet default" , " Confirmation target (in blocks)" },
3088
+ {" conf_target" , RPCArg::Type::NUM, /* default */ " wallet default" , " Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + " /kB or " + CURRENCY_ATOM + " /B estimate modes) " },
3072
3089
{" estimate_mode" , RPCArg::Type::STR, /* default */ " unset" , std::string () + " The fee estimate mode, must be one of (case insensitive):\n "
3073
3090
" \" " + FeeModes (" \"\n\" " ) + " \" " },
3074
3091
},
@@ -3315,11 +3332,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
3315
3332
if (options.exists (" replaceable" )) {
3316
3333
coin_control.m_signal_bip125_rbf = options[" replaceable" ].get_bool ();
3317
3334
}
3318
- if (options.exists (" estimate_mode" )) {
3319
- if (!FeeModeFromString (options[" estimate_mode" ].get_str (), coin_control.m_fee_mode )) {
3320
- throw JSONRPCError (RPC_INVALID_PARAMETER, " Invalid estimate_mode parameter" );
3321
- }
3322
- }
3335
+ SetFeeEstimateMode (pwallet, coin_control, options[" estimate_mode" ], conf_target);
3323
3336
}
3324
3337
3325
3338
// Make sure the results are valid at least up to the most recent block
0 commit comments