LCOV - code coverage report
Current view: top level - src/wallet/rpc - spend.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 878 921 95.3 %
Date: 2026-06-25 07:23:43 Functions: 30 30 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2011-2021 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <consensus/validation.h>
       6             : #include <core_io.h>
       7             : #include <key_io.h>
       8             : #include <policy/policy.h>
       9             : #include <rpc/rawtransaction_util.h>
      10             : #include <rpc/util.h>
      11             : #include <util/fees.h>
      12             : #include <util/translation.h>
      13             : #include <util/vector.h>
      14             : #include <wallet/coincontrol.h>
      15             : #include <wallet/fees.h>
      16             : #include <wallet/rpc/util.h>
      17             : #include <wallet/spend.h>
      18             : #include <wallet/wallet.h>
      19             : 
      20             : #include <univalue.h>
      21             : 
      22             : #include <map>
      23             : 
      24             : namespace wallet {
      25        5058 : static void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient>& recipients)
      26             : {
      27        5058 :     std::set<CTxDestination> destinations;
      28        5058 :     int i = 0;
      29       23025 :     for (const std::string& address: address_amounts.getKeys()) {
      30       17979 :         CTxDestination dest = DecodeDestination(address);
      31       17979 :         if (!IsValidDestination(dest)) {
      32           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + address);
      33             :         }
      34             : 
      35       17979 :         if (destinations.count(dest)) {
      36           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + address);
      37             :         }
      38       17979 :         destinations.insert(dest);
      39             : 
      40       17979 :         CScript script_pub_key = GetScriptForDestination(dest);
      41       17979 :         CAmount amount = AmountFromValue(address_amounts[i++]);
      42             : 
      43       17967 :         bool subtract_fee = false;
      44       18048 :         for (unsigned int idx = 0; idx < subtract_fee_outputs.size(); idx++) {
      45          81 :             const UniValue& addr = subtract_fee_outputs[idx];
      46          81 :             if (addr.get_str() == address) {
      47          69 :                 subtract_fee = true;
      48          69 :             }
      49          81 :         }
      50             : 
      51       17967 :         CRecipient recipient = {script_pub_key, amount, subtract_fee};
      52       17967 :         recipients.push_back(recipient);
      53       17979 :     }
      54        5070 : }
      55             : 
      56         706 : static void InterpretFeeEstimationInstructions(const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, UniValue& options)
      57             : {
      58         722 :     if (options.exists("conf_target") || options.exists("estimate_mode")) {
      59          96 :         if (!conf_target.isNull() || !estimate_mode.isNull()) {
      60          12 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Pass conf_target and estimate_mode either as arguments or in the options object, but not both");
      61             :         }
      62          84 :     } else {
      63         610 :         options.pushKV("conf_target", conf_target);
      64         610 :         options.pushKV("estimate_mode", estimate_mode);
      65             :     }
      66         694 :     if (options.exists("fee_rate")) {
      67         116 :         if (!fee_rate.isNull()) {
      68           4 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Pass the fee_rate either as an argument, or in the options object, but not both");
      69             :         }
      70         112 :     } else {
      71         578 :         options.pushKV("fee_rate", fee_rate);
      72             :     }
      73         806 :     if (!options["conf_target"].isNull() && (options["estimate_mode"].isNull() || (options["estimate_mode"].get_str() == "unset"))) {
      74           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Specify estimate_mode");
      75             :     }
      76         706 : }
      77             : 
      78         471 : static UniValue FinishTransaction(const std::shared_ptr<CWallet> pwallet, const UniValue& options, const CMutableTransaction& rawTx)
      79             : {
      80             :     // Make a blank psbt
      81         471 :     PartiallySignedTransaction psbtx(rawTx);
      82             : 
      83             :     // First fill transaction with our data without signing,
      84             :     // so external signers are not asked sign more than once.
      85             :     bool complete;
      86         471 :     (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true);
      87         283 :     const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
      88         283 :     if (err != TransactionError::OK) {
      89           0 :         throw JSONRPCTransactionError(err);
      90             :     }
      91             : 
      92         283 :     CMutableTransaction mtx;
      93         471 :     complete = FinalizeAndExtractPSBT(psbtx, mtx);
      94             : 
      95         471 :     UniValue result(UniValue::VOBJ);
      96             : 
      97         487 :     const bool psbt_opt_in{options.exists("psbt") && options["psbt"].get_bool()};
      98         283 :     bool add_to_wallet{options.exists("add_to_wallet") ? options["add_to_wallet"].get_bool() : true};
      99         283 :     if (psbt_opt_in || !complete || !add_to_wallet) {
     100             :         // Serialize the PSBT
     101          30 :         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
     102         124 :         ssTx << psbtx;
     103         124 :         result.pushKV("psbt", EncodeBase64(ssTx.str()));
     104         124 :     }
     105             : 
     106         377 :     if (complete) {
     107         257 :         std::string hex{EncodeHexTx(CTransaction(mtx))};
     108         257 :         CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
     109         257 :         result.pushKV("txid", tx->GetHash().GetHex());
     110         257 :         if (add_to_wallet && !psbt_opt_in) {
     111         159 :             pwallet->CommitTransaction(tx, {}, /*orderForm*/ {});
     112         159 :         } else {
     113          98 :             result.pushKV("hex", hex);
     114             :         }
     115         257 :     }
     116         377 :     result.pushKV("complete", complete);
     117             : 
     118         283 :     return result;
     119        1035 : }
     120             : 
     121         690 : static void PreventOutdatedOptions(const UniValue& options)
     122             : {
     123         694 :     if (options.exists("feeRate")) {
     124           4 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use fee_rate (" + CURRENCY_ATOM + "/B) instead of feeRate");
     125             :     }
     126         686 :     if (options.exists("changeAddress")) {
     127           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_address instead of changeAddress");
     128             :     }
     129         686 :     if (options.exists("changePosition")) {
     130           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_position instead of changePosition");
     131             :     }
     132         686 :     if (options.exists("includeWatching")) {
     133           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use include_watching instead of includeWatching");
     134             :     }
     135         686 :     if (options.exists("lockUnspents")) {
     136           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use lock_unspents instead of lockUnspents");
     137             :     }
     138         686 :     if (options.exists("subtractFeeFromOutputs")) {
     139           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use subtract_fee_from_outputs instead of subtractFeeFromOutputs");
     140             :     }
     141         690 : }
     142             : 
     143        5046 : UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
     144             : {
     145        5046 :     EnsureWalletIsUnlocked(wallet);
     146             : 
     147             :     // This function is only used by sendtoaddress and sendmany.
     148             :     // This should always try to sign, if we don't have private keys, don't try to do anything here.
     149        5046 :     if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     150         109 :         throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
     151             :     }
     152             : 
     153        5038 :     if (coin_control.IsUsingCoinJoin()) {
     154           0 :         map_value["DS"] = "1";
     155           0 :     }
     156             : 
     157             :     // Send
     158        5038 :     auto res = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, coin_control, /*sign=*/true);
     159        5038 :     if (!res) {
     160         101 :         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, util::ErrorString(res).original);
     161             :     }
     162        4937 :     const CTransactionRef& tx = res->tx;
     163        4937 :     wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
     164        4937 :     if (verbose) {
     165          12 :         UniValue entry(UniValue::VOBJ);
     166          12 :         entry.pushKV("txid", tx->GetHash().GetHex());
     167          12 :         entry.pushKV("fee_reason", StringForFeeReason(res->fee_calc.reason));
     168          12 :         return entry;
     169          12 :     }
     170        4925 :     return tx->GetHash().GetHex();
     171        5147 : }
     172             : 
     173             : /**
     174             :  * Update coin control with fee estimation based on the given parameters
     175             :  *
     176             :  * @param[in]     wallet            Wallet reference
     177             :  * @param[in,out] cc                Coin control to be updated
     178             :  * @param[in]     conf_target       UniValue integer; confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
     179             :  * @param[in]     estimate_mode     UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative";
     180             :  * @param[in]     fee_rate          UniValue real; fee rate in sat/B;
     181             :  *                                      if present, both conf_target and estimate_mode must either be null, or no-op or "unset"
     182             :  * @param[in]     override_min_fee  bool; whether to set fOverrideFeeRate to true to disable minimum fee rate checks and instead
     183             :  *                                      verify only that fee_rate is greater than 0
     184             :  * @throws a JSONRPCError if conf_target, estimate_mode, or fee_rate contain invalid values or are in conflict
     185             :  */
     186        6716 : static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee)
     187             : {
     188        6716 :     if (!fee_rate.isNull()) {
     189         390 :         if (!conf_target.isNull()) {
     190         153 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
     191             :         }
     192         377 :         if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") {
     193          13 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate");
     194             :         }
     195             :         // Fee rates in sat/vB cannot represent more than 3 significant digits.
     196         364 :         cc.m_feerate = CFeeRate{AmountFromValue(fee_rate, /*decimals=*/3)};
     197         364 :         if (override_min_fee) cc.fOverrideFeeRate = true;
     198         364 :         return;
     199             :     }
     200        6326 :     if (!estimate_mode.isNull() && !FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
     201         127 :         throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
     202             :     }
     203        6199 :     if (!conf_target.isNull()) {
     204         203 :         cc.m_confirm_target = ParseConfirmTarget(conf_target, wallet.chain().estimateMaxBlocks());
     205         203 :     }
     206        6716 : }
     207             : 
     208        7835 : RPCHelpMan sendtoaddress()
     209             : {
     210       15670 :     return RPCHelpMan{"sendtoaddress",
     211        7835 :         "\nSend an amount to a given address." +
     212             :         HELP_REQUIRING_PASSPHRASE,
     213      101855 :                 {
     214        7835 :                     {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Dash address to send to."},
     215        7835 :                     {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
     216        7835 :                     {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment used to store what the transaction is for.\n"
     217             :                                          "This is not part of the transaction, just kept in your wallet."},
     218        7835 :                     {"comment_to", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment to store the name of the person or organization\n"
     219             :                                          "to which you're sending the transaction. This is not part of the \n"
     220             :                                          "transaction, just kept in your wallet."},
     221        7835 :                     {"subtractfeefromamount", RPCArg::Type::BOOL, RPCArg::Default{false}, "The fee will be deducted from the amount being sent.\n"
     222             :                                          "The recipient will receive less amount of Dash than you enter in the amount field."},
     223        7835 :                     {"use_is", RPCArg::Type::BOOL, RPCArg::Default{false}, "Deprecated and ignored"},
     224        7835 :                     {"use_cj", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use CoinJoin funds only"},
     225        7835 :                     {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
     226       15670 :                     {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
     227        7835 :                     "       \"" + FeeModes("\"\n\"") + "\""},
     228        7835 :                     {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
     229             :                                          "dirty if they have previously been used in a transaction."},
     230        7835 :                     {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     231        7835 :                     {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra information about the transaction."},
     232             :                 },
     233       23505 :                 {
     234       15670 :                     RPCResult{"if verbose is not set or set to false",
     235        7835 :                         RPCResult::Type::STR_HEX, "txid", "The transaction id."
     236             :                     },
     237       15670 :                     RPCResult{"if verbose is set to true",
     238        7835 :                         RPCResult::Type::OBJ, "", "",
     239       23505 :                         {
     240        7835 :                             {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
     241        7835 :                             {RPCResult::Type::STR, "fee_reason", "The transaction fee reason."}
     242             :                         },
     243             :                     },
     244             :                 },
     245        7835 :                 RPCExamples{
     246             :                     "\nSend 0.1 Dash\n"
     247        7835 :                     + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1") +
     248             :                     "\nSend 0.1 Dash with a confirmation target of 6 blocks in economical fee estimate mode using positional arguments\n"
     249        7835 :                     + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"sean's outpost\" false false false 6 economical") +
     250        7835 :                     "\nSend 0.1 Dash with a fee rate of 1.1 " + CURRENCY_ATOM + "/B, subtract fee from amount, use CoinJoin funds only, using positional arguments\n"
     251        7835 :                     + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"drinks\" \"room77\" true false true 0 \"\" 1.1") +
     252             :                     "\nSend 0.2 Dash with a confirmation target of 6 blocks in economical fee estimate mode using named arguments\n"
     253        7835 :                     + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.2 conf_target=6 estimate_mode=\"economical\"") +
     254        7835 :                     "\nSend 0.5 Dash with a fee rate of 25 " + CURRENCY_ATOM + "/B using named arguments\n"
     255        7835 :                     + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.5 fee_rate=25")
     256        7835 :                     + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.5 fee_rate=25 subtractfeefromamount=false avoid_reuse=true comment=\"2 pizzas\" comment_to=\"jeremy\" verbose=true")
     257             :                 },
     258       12774 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     259             : {
     260        4939 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     261        4939 :     if (!pwallet) return UniValue::VNULL;
     262             : 
     263             :     // Make sure the results are valid at least up to the most recent block
     264             :     // the user could have gotten from another RPC command prior to now
     265        4939 :     pwallet->BlockUntilSyncedToCurrentChain();
     266             : 
     267        4939 :     LOCK(pwallet->cs_wallet);
     268             : 
     269             :     // Wallet comments
     270        4939 :     mapValue_t mapValue;
     271        4939 :     if (!request.params[2].isNull() && !request.params[2].get_str().empty())
     272           0 :         mapValue["comment"] = request.params[2].get_str();
     273        4939 :     if (!request.params[3].isNull() && !request.params[3].get_str().empty())
     274           0 :         mapValue["to"] = request.params[3].get_str();
     275             : 
     276        4939 :     bool fSubtractFeeFromAmount = false;
     277        4939 :     if (!request.params[4].isNull()) {
     278          51 :         fSubtractFeeFromAmount = request.params[4].get_bool();
     279          51 :     }
     280             : 
     281        4939 :     CCoinControl coin_control;
     282             : 
     283        4939 :     if (!request.params[6].isNull()) {
     284           0 :         coin_control.UseCoinJoin(request.params[6].get_bool());
     285           0 :     }
     286             : 
     287        4939 :     coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(*pwallet, request.params[9]);
     288             :     // We also enable partial spend avoidance if reuse avoidance is set.
     289        4939 :     coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
     290             : 
     291        4939 :     SetFeeEstimateMode(*pwallet, coin_control, /*conf_target=*/request.params[7], /*estimate_mode=*/request.params[8], /*fee_rate=*/request.params[10], /*override_min_fee=*/false);
     292             : 
     293        4858 :     EnsureWalletIsUnlocked(*pwallet);
     294             : 
     295        4849 :     UniValue address_amounts(UniValue::VOBJ);
     296        4849 :     const std::string address = request.params[0].get_str();
     297        4849 :     address_amounts.pushKV(address, request.params[1]);
     298        4849 :     UniValue subtractFeeFromAmount(UniValue::VARR);
     299        4849 :     if (fSubtractFeeFromAmount) {
     300          45 :         subtractFeeFromAmount.push_back(address);
     301          45 :     }
     302             : 
     303        4849 :     std::vector<CRecipient> recipients;
     304        4849 :     ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
     305        4837 :     const bool verbose{request.params[11].isNull() ? false : request.params[11].get_bool()};
     306             : 
     307        4837 :     return SendMoney(*pwallet, coin_control, recipients, mapValue, verbose);
     308        4939 : },
     309             :     };
     310           0 : }
     311             : 
     312        3267 : RPCHelpMan sendmany()
     313             : {
     314        6534 :     return RPCHelpMan{"sendmany",
     315        3267 :                 "\nSend multiple times. Amounts are double-precision floating point numbers." +
     316             :         HELP_REQUIRING_PASSPHRASE,
     317       42471 :                 {
     318        3267 :                     {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
     319        6534 :                     {"amounts", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::NO, "The addresses and amounts",
     320        6534 :                         {
     321        3267 :                             {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The Dash address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
     322             :                         },
     323             :                     },
     324        3267 :                     {"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
     325        3267 :                     {"addlocked", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
     326        3267 :                     {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
     327        6534 :                     {"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The addresses.\n"
     328             :                                        "The fee will be equally deducted from the amount of each selected address.\n"
     329             :                                        "Those recipients will receive less Dash than you enter in their corresponding amount field.\n"
     330             :                                        "If no addresses are specified here, the sender pays the fee.",
     331        6534 :                         {
     332        3267 :                             {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
     333             :                         },
     334             :                     },
     335        3267 :                     {"use_is", RPCArg::Type::BOOL, RPCArg::Default{false}, "Deprecated and ignored"},
     336        3267 :                     {"use_cj", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use CoinJoin funds only"},
     337        3267 :                     {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
     338        6534 :                     {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
     339        3267 :             "       \"" + FeeModes("\"\n\"") + "\""},
     340        3267 :                     {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     341        3267 :                     {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra information about the transaction."},
     342             :                 },
     343        9801 :                 {
     344        6534 :                     RPCResult{"if verbose is not set or set to false",
     345        3267 :                         RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
     346             :                 "the number of addresses."
     347             :                     },
     348        6534 :                     RPCResult{"if verbose is set to true",
     349        3267 :                         RPCResult::Type::OBJ, "", "",
     350        9801 :                         {
     351        3267 :                             {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
     352             :                 "the number of addresses."},
     353        3267 :                             {RPCResult::Type::STR, "fee_reason", "The transaction fee reason."}
     354             :                         },
     355             :                     },
     356             :                 },
     357        3267 :                 RPCExamples{
     358             :             "\nSend two amounts to two different addresses:\n"
     359        3267 :             + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\"") +
     360             :             "\nSend two amounts to two different addresses setting the confirmation and comment:\n"
     361        3267 :             + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\" 6 false \"testing\"") +
     362             :             "\nAs a json rpc call\n"
     363        3267 :             + HelpExampleRpc("sendmany", "\"\", \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\", 6, false, \"testing\"")
     364             :                 },
     365        3638 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     366             : {
     367         371 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     368         371 :     if (!pwallet) return UniValue::VNULL;
     369             : 
     370             :     // Make sure the results are valid at least up to the most recent block
     371             :     // the user could have gotten from another RPC command prior to now
     372         371 :     pwallet->BlockUntilSyncedToCurrentChain();
     373             : 
     374         371 :     LOCK(pwallet->cs_wallet);
     375             : 
     376         371 :     if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
     377           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
     378             :     }
     379         371 :     UniValue sendTo = request.params[1].get_obj();
     380         371 :     mapValue_t mapValue;
     381         371 :     if (!request.params[4].isNull() && !request.params[4].get_str().empty())
     382           0 :         mapValue["comment"] = request.params[4].get_str();
     383             : 
     384         371 :     UniValue subtractFeeFromAmount(UniValue::VARR);
     385         371 :     if (!request.params[5].isNull())
     386          24 :         subtractFeeFromAmount = request.params[5].get_array();
     387             : 
     388             :     // request.params[6] ("use_is") is deprecated and not used here
     389             : 
     390         371 :     CCoinControl coin_control;
     391             : 
     392         371 :     if (!request.params[7].isNull()) {
     393           0 :         coin_control.UseCoinJoin(request.params[7].get_bool());
     394           0 :     }
     395             : 
     396         371 :     SetFeeEstimateMode(*pwallet, coin_control, /*conf_target=*/request.params[8], /*estimate_mode=*/request.params[9], /*fee_rate=*/request.params[10], /*override_min_fee=*/false);
     397             : 
     398         209 :     std::vector<CRecipient> recipients;
     399         209 :     ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
     400         209 :     const bool verbose{request.params[11].isNull() ? false : request.params[11].get_bool()};
     401             : 
     402         209 :     return SendMoney(*pwallet, coin_control, recipients, std::move(mapValue), verbose);
     403         371 : },
     404             :     };
     405           0 : }
     406             : 
     407        2956 : RPCHelpMan settxfee()
     408             : {
     409        5912 :     return RPCHelpMan{"settxfee",
     410        2956 :                 "\nSet the transaction fee rate in " + CURRENCY_UNIT + "/kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
     411             :                 "Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default.\n",
     412        5912 :                 {
     413        2956 :                     {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee rate in " + CURRENCY_UNIT + "/kB"},
     414             :                 },
     415        2956 :                 RPCResult{
     416        2956 :                     RPCResult::Type::BOOL, "", "Returns true if successful"
     417             :                 },
     418        2956 :                 RPCExamples{
     419        2956 :                     HelpExampleCli("settxfee", "0.00001")
     420        2956 :             + HelpExampleRpc("settxfee", "0.00001")
     421             :                 },
     422        3016 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     423             : {
     424          60 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     425          60 :     if (!pwallet) return UniValue::VNULL;
     426             : 
     427          60 :     LOCK(pwallet->cs_wallet);
     428             : 
     429          60 :     CAmount nAmount = AmountFromValue(request.params[0]);
     430          60 :     CFeeRate tx_fee_rate(nAmount, 1000);
     431          60 :     CFeeRate max_tx_fee_rate(pwallet->m_default_max_tx_fee, 1000);
     432          60 :     if (tx_fee_rate == CFeeRate(0)) {
     433             :         // automatic selection
     434          60 :     } else if (tx_fee_rate < pwallet->chain().relayMinFee()) {
     435           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", pwallet->chain().relayMinFee().ToString()));
     436          56 :     } else if (tx_fee_rate < pwallet->m_min_fee) {
     437           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than wallet min fee (%s)", pwallet->m_min_fee.ToString()));
     438          56 :     } else if (tx_fee_rate > max_tx_fee_rate) {
     439           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be more than wallet max tx fee (%s)", max_tx_fee_rate.ToString()));
     440             :     }
     441             : 
     442          60 :     pwallet->m_pay_tx_fee = tx_fee_rate;
     443          60 :     return true;
     444          60 : },
     445             :     };
     446           0 : }
     447             : 
     448             : // Only includes key documentation where the key is snake_case in all RPC methods. MixedCase keys can be added later.
     449       14192 : static std::vector<RPCArg> FundTxDoc(bool solving_data = true)
     450             : {
     451       42576 :     std::vector<RPCArg> args = {
     452       14192 :         {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
     453       28384 :         {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
     454       14192 :             "         \"" + FeeModes("\"\n\"") + "\""},
     455             :     };
     456       14192 :     if (solving_data) {
     457       28384 :         args.push_back({"solving_data", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "Keys and scripts needed for producing a final transaction with a dummy signature.\n"
     458             :         "Used for fee estimation during coin selection.",
     459       56768 :             {
     460       14192 :                 {
     461       14192 :                     "pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Public keys involved in this transaction.",
     462       28384 :                     {
     463       14192 :                         {"pubkey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A public key"},
     464             :                     }
     465             :                 },
     466       14192 :                 {
     467       14192 :                     "scripts", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Scripts involved in this transaction.",
     468       28384 :                     {
     469       14192 :                         {"script", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A script"},
     470             :                     }
     471             :                 },
     472       14192 :                 {
     473       14192 :                     "descriptors", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Descriptors that provide solving data for this transaction.",
     474       28384 :                     {
     475       14192 :                         {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A descriptor"},
     476             :                     }
     477             :                 },
     478             :             }
     479             :         });
     480       14192 :     }
     481       14192 :     return args;
     482       14192 : }
     483             : 
     484        2400 : void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
     485             : {
     486             :     // Make sure the results are valid at least up to the most recent block
     487             :     // the user could have gotten from another RPC command prior to now
     488        2400 :     wallet.BlockUntilSyncedToCurrentChain();
     489             : 
     490        2400 :     change_position = -1;
     491        2400 :     bool lockUnspents = false;
     492        2400 :     UniValue subtractFeeFromOutputs;
     493        2400 :     std::set<int> setSubtractFeeFromOutputs;
     494             : 
     495        2400 :     if (!options.isNull()) {
     496        2050 :       if (options.type() == UniValue::VBOOL) {
     497             :         // backward compatibility bool only fallback
     498           9 :         coinControl.fAllowWatchOnly = options.get_bool();
     499           9 :       }
     500             :       else {
     501        4082 :         RPCTypeCheckObj(options,
     502       46943 :             {
     503        2041 :                 {"add_inputs", UniValueType(UniValue::VBOOL)},
     504        2041 :                 {"include_unsafe", UniValueType(UniValue::VBOOL)},
     505        2041 :                 {"add_to_wallet", UniValueType(UniValue::VBOOL)},
     506        2041 :                 {"changeAddress", UniValueType(UniValue::VSTR)},
     507        2041 :                 {"change_address", UniValueType(UniValue::VSTR)},
     508        2041 :                 {"changePosition", UniValueType(UniValue::VNUM)},
     509        2041 :                 {"change_position", UniValueType(UniValue::VNUM)},
     510        2041 :                 {"includeWatching", UniValueType(UniValue::VBOOL)},
     511        2041 :                 {"include_watching", UniValueType(UniValue::VBOOL)},
     512        2041 :                 {"inputs", UniValueType(UniValue::VARR)},
     513        2041 :                 {"lockUnspents", UniValueType(UniValue::VBOOL)},
     514        2041 :                 {"lock_unspents", UniValueType(UniValue::VBOOL)},
     515        2041 :                 {"locktime", UniValueType(UniValue::VNUM)},
     516        2041 :                 {"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
     517        2041 :                 {"feeRate", UniValueType()}, // will be checked by AmountFromValue() below
     518        2041 :                 {"psbt", UniValueType(UniValue::VBOOL)},
     519        2041 :                 {"solving_data", UniValueType(UniValue::VOBJ)},
     520        2041 :                 {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
     521        2041 :                 {"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
     522        2041 :                 {"conf_target", UniValueType(UniValue::VNUM)},
     523        2041 :                 {"estimate_mode", UniValueType(UniValue::VSTR)},
     524        2041 :                 {"input_sizes", UniValueType(UniValue::VARR)},
     525             :             },
     526             :             true, true);
     527             : 
     528        1891 :         if (options.exists("add_inputs")) {
     529         816 :             coinControl.m_allow_other_inputs = options["add_inputs"].get_bool();
     530         816 :         }
     531             : 
     532        1891 :         if (options.exists("changeAddress") || options.exists("change_address")) {
     533          85 :             const std::string change_address_str = (options.exists("change_address") ? options["change_address"] : options["changeAddress"]).get_str();
     534          85 :             CTxDestination dest = DecodeDestination(change_address_str);
     535             : 
     536          85 :             if (!IsValidDestination(dest)) {
     537          13 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid Dash address");
     538             :             }
     539             : 
     540          72 :             coinControl.destChange = dest;
     541          85 :         }
     542             : 
     543        1878 :         if (options.exists("changePosition") || options.exists("change_position")) {
     544          69 :             change_position = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).getInt<int>();
     545          69 :         }
     546             : 
     547        1878 :         const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
     548        1878 :         coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, wallet);
     549             : 
     550        1878 :         if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
     551          20 :             lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
     552          20 :         }
     553             : 
     554        1878 :         if (options.exists("include_unsafe")) {
     555          26 :             coinControl.m_include_unsafe_inputs = options["include_unsafe"].get_bool();
     556          26 :         }
     557             : 
     558        1878 :         if (options.exists("feeRate")) {
     559         327 :             if (options.exists("fee_rate")) {
     560          13 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both fee_rate (" + CURRENCY_ATOM + "/B) and feeRate (" + CURRENCY_UNIT + "/kB)");
     561             :             }
     562         314 :             if (options.exists("conf_target")) {
     563          13 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
     564             :             }
     565         301 :             if (options.exists("estimate_mode")) {
     566          13 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
     567             :             }
     568         288 :             coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
     569         184 :             coinControl.fOverrideFeeRate = true;
     570         184 :         }
     571             : 
     572        1735 :         if (options.exists("subtractFeeFromOutputs") || options.exists("subtract_fee_from_outputs") )
     573         123 :             subtractFeeFromOutputs = (options.exists("subtract_fee_from_outputs") ? options["subtract_fee_from_outputs"] : options["subtractFeeFromOutputs"]).get_array();
     574             : 
     575        1735 :         SetFeeEstimateMode(wallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
     576        1878 :       }
     577        1142 :     } else {
     578             :         // if options is null and not a bool
     579         350 :         coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
     580             :     }
     581             : 
     582        1492 :     if (options.exists("solving_data")) {
     583          83 :         const UniValue solving_data = options["solving_data"].get_obj();
     584          83 :         if (solving_data.exists("pubkeys")) {
     585          52 :             for (const UniValue& pk_univ : solving_data["pubkeys"].get_array().getValues()) {
     586          35 :                 const std::string& pk_str = pk_univ.get_str();
     587          35 :                 if (!IsHex(pk_str)) {
     588           9 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", pk_str));
     589             :                 }
     590          26 :                 const std::vector<unsigned char> data(ParseHex(pk_str));
     591          26 :                 const CPubKey pubkey(data.begin(), data.end());
     592          26 :                 if (!pubkey.IsFullyValid()) {
     593           9 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not a valid public key", pk_str));
     594             :                 }
     595          17 :                 coinControl.m_external_provider.pubkeys.emplace(pubkey.GetID(), pubkey);
     596             :                 // Add script for pubkeys
     597          17 :                 const CScript pk_script = GetScriptForDestination(PKHash(pubkey));
     598          17 :                 coinControl.m_external_provider.scripts.emplace(CScriptID(pk_script), pk_script);
     599          26 :             }
     600          17 :         }
     601             : 
     602          65 :         if (solving_data.exists("scripts")) {
     603          43 :             for (const UniValue& script_univ : solving_data["scripts"].get_array().getValues()) {
     604          26 :                 const std::string& script_str = script_univ.get_str();
     605          26 :                 if (!IsHex(script_str)) {
     606           9 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", script_str));
     607             :                 }
     608          17 :                 std::vector<unsigned char> script_data(ParseHex(script_str));
     609          17 :                 const CScript script(script_data.begin(), script_data.end());
     610          17 :                 coinControl.m_external_provider.scripts.emplace(CScriptID(script), script);
     611          17 :             }
     612          17 :         }
     613             : 
     614          56 :         if (solving_data.exists("descriptors")) {
     615          69 :             for (const UniValue& desc_univ : solving_data["descriptors"].get_array().getValues()) {
     616          39 :                 const std::string& desc_str  = desc_univ.get_str();
     617          39 :                 FlatSigningProvider desc_out;
     618          39 :                 std::string error;
     619          39 :                 std::vector<CScript> scripts_temp;
     620          39 :                 std::unique_ptr<Descriptor> desc = Parse(desc_str, desc_out, error, true);
     621          39 :                 if (!desc) {
     622           9 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unable to parse descriptor '%s': %s", desc_str, error));
     623             :                 }
     624          30 :                 desc->Expand(0, desc_out, scripts_temp, desc_out);
     625          30 :                 coinControl.m_external_provider.Merge(std::move(desc_out));
     626          39 :             }
     627          30 :         }
     628          83 :     }
     629             : 
     630        1456 :     if (options.exists("input_sizes")) {
     631         446 :         for (const UniValue& input : options["input_sizes"].get_array().getValues()) {
     632         141 :             uint256 txid = ParseHashO(input, "txid");
     633             : 
     634         141 :             const UniValue& vout_v = input.find_value("vout");
     635         141 :             if (!vout_v.isNum()) {
     636           9 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
     637             :             }
     638         132 :             int vout = vout_v.getInt<int>();
     639         132 :             if (vout < 0) {
     640           9 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
     641             :             }
     642             : 
     643         123 :             const UniValue& weight_v = input.find_value("size");
     644         123 :             if (!weight_v.isNum()) {
     645           9 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing size key");
     646             :             }
     647         114 :             int64_t size = weight_v.getInt<int64_t>();
     648         114 :             const int64_t min_input_size = ::GetSerializeSize(CTxIn());
     649         114 :             CHECK_NONFATAL(min_input_size == 41);
     650         114 :             if (size < min_input_size) {
     651          18 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, size cannot be less than 41 bytes (size of outpoint + sequence + empty scriptSig)");
     652             :             }
     653          96 :             if (size > MAX_STANDARD_TX_SIZE) {
     654           9 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size cannot be greater than the maximum standard tx size of %d", MAX_STANDARD_TX_SIZE));
     655             :             }
     656             : 
     657          87 :             coinControl.SetInputWeight(COutPoint(txid, vout), size);
     658             :         }
     659         305 :     }
     660             : 
     661        1402 :     if (tx.vout.size() == 0)
     662           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
     663             : 
     664        1402 :     if (change_position != -1 && (change_position < 0 || (unsigned int)change_position > tx.vout.size()))
     665           9 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
     666             : 
     667        1516 :     for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
     668         123 :         int pos = subtractFeeFromOutputs[idx].getInt<int>();
     669         123 :         if (setSubtractFeeFromOutputs.count(pos))
     670           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos));
     671         123 :         if (pos < 0)
     672           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos));
     673         123 :         if (pos >= int(tx.vout.size()))
     674           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos));
     675         123 :         setSubtractFeeFromOutputs.insert(pos);
     676         123 :     }
     677             : 
     678        1393 :     bilingual_str error;
     679             : 
     680        1393 :     if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
     681         254 :         throw JSONRPCError(RPC_WALLET_ERROR, error.original);
     682             :     }
     683        3407 : }
     684             : 
     685         978 : static void SetOptionsInputWeights(const UniValue& inputs, UniValue& options)
     686             : {
     687         986 :     if (options.exists("input_sizes")) {
     688           8 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Input sizes should be specified in inputs rather than in options.");
     689             :     }
     690         970 :     if (inputs.size() == 0) {
     691         532 :         return;
     692             :     }
     693         438 :     UniValue sizes(UniValue::VARR);
     694         894 :     for (const UniValue& input : inputs.getValues()) {
     695         456 :         if (input.exists("size")) {
     696          24 :             sizes.push_back(input);
     697          24 :         }
     698             :     }
     699         438 :     options.pushKV("input_sizes", sizes);
     700         978 : }
     701             : 
     702        4326 : RPCHelpMan fundrawtransaction()
     703             : {
     704        8652 :     return RPCHelpMan{"fundrawtransaction",
     705        4326 :                 "\nIf the transaction has no inputs, they will be automatically selected to meet its out value.\n"
     706             :                 "It will add at most one change output to the outputs.\n"
     707             :                 "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
     708             :                 "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
     709             :                 "The inputs added will not be signed, use signrawtransactionwithkey\n"
     710             :                 "or signrawtransactionwithwallet for that.\n"
     711             :                 "All existing inputs must either have their previous output transaction be in the wallet\n"
     712             :                 "or be in the UTXO set. Solving data must be provided for non-wallet inputs.\n"
     713             :                 "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
     714             :                 "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
     715             :                 "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
     716             :                 "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n",
     717       12978 :                 {
     718        4326 :                     {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
     719        8652 :                     {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
     720        4326 :                         Cat<std::vector<RPCArg>>(
     721       47586 :                         {
     722        4326 :                             {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{true}, "For a transaction with existing inputs, automatically include more if they are not enough."},
     723        4326 :                             {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
     724             :                                                           "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
     725             :                                                           "If that happens, you will need to fund the transaction with different inputs and republish it."},
     726        4326 :                             {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The Dash address to receive the change"},
     727        4326 :                             {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
     728        4326 :                             {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
     729             :                                                           "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
     730             :                                                           "e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
     731        4326 :                             {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
     732        4326 :                             {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     733        4326 :                             {"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kB."},
     734        8652 :                             {"subtractFeeFromOutputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The integers.\n"
     735             :                                                           "The fee will be equally deducted from the amount of each specified output.\n"
     736             :                                                           "Those recipients will receive less Dash than you enter in their corresponding amount field.\n"
     737             :                                                           "If no outputs are specified here, the sender pays the fee.",
     738        8652 :                                 {
     739        4326 :                                     {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
     740             :                                 },
     741             :                             },
     742        8652 :                             {"input_sizes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding sizes",
     743        8652 :                                 {
     744        8652 :                                     {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
     745       17304 :                                         {
     746        4326 :                                             {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
     747        4326 :                                             {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
     748        4326 :                                             {"size", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum size for this input, "
     749             :                                                 "including the size of the outpoint and sequence number. "
     750             :                                                 "Note that serialized signature sizes are not guaranteed to be consistent, "
     751             :                                                 "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."},
     752             :                                         },
     753             :                                     },
     754             :                                 },
     755             :                              },
     756             :                         },
     757        4326 :                         FundTxDoc()),
     758        4326 :                         "options"},
     759             :                 },
     760        4326 :                 RPCResult{
     761        4326 :                     RPCResult::Type::OBJ, "", "",
     762       17304 :                     {
     763        4326 :                         {RPCResult::Type::STR_HEX, "hex", "The resulting raw transaction (hex-encoded string)"},
     764        4326 :                         {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
     765        4326 :                         {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
     766             :                     }
     767             :                                 },
     768        4326 :                                 RPCExamples{
     769             :                             "\nCreate a transaction with no inputs\n"
     770        4326 :                             + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
     771             :                             "\nAdd sufficient unsigned inputs to meet the output value\n"
     772        4326 :                             + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
     773             :                             "\nSign the transaction\n"
     774        4326 :                             + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
     775             :                             "\nSend the transaction\n"
     776        4326 :                             + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
     777             :                                 },
     778        5756 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     779             : {
     780        2111 :     RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
     781             : 
     782        1430 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     783        1430 :     if (!pwallet) return UniValue::VNULL;
     784             : 
     785             :     // parse hex string from parameter
     786        1430 :     CMutableTransaction tx;
     787        1430 :     if (!DecodeHexTx(tx, request.params[0].get_str())) {
     788           0 :         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
     789             :     }
     790             : 
     791             :     CAmount fee;
     792             :     int change_position;
     793        1430 :     CCoinControl coin_control;
     794             :     // Automatically select (additional) coins. Can be overridden by options.add_inputs.
     795        1430 :     coin_control.m_allow_other_inputs = true;
     796        1430 :     FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control, /*override_min_fee=*/true);
     797             : 
     798         749 :     UniValue result(UniValue::VOBJ);
     799         749 :     result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
     800         749 :     result.pushKV("fee", ValueFromAmount(fee));
     801         749 :     result.pushKV("changepos", change_position);
     802             : 
     803         749 :     return result;
     804        1430 : },
     805             :     };
     806           0 : }
     807             : 
     808        4554 : RPCHelpMan signrawtransactionwithwallet()
     809             : {
     810        9108 :     return RPCHelpMan{"signrawtransactionwithwallet",
     811             :                 "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
     812             :                 "The second optional argument (may be null) is an array of previous transaction outputs that\n"
     813        4554 :                 "this transaction depends on but may not yet be in the block chain." +
     814             :         HELP_REQUIRING_PASSPHRASE,
     815       18216 :                 {
     816        4554 :                     {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
     817        9108 :                     {"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The previous dependent transaction outputs",
     818        9108 :                         {
     819        9108 :                             {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
     820       27324 :                                 {
     821        4554 :                                     {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
     822        4554 :                                     {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
     823        4554 :                                     {"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
     824        4554 :                                     {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH)"},
     825        4554 :                                     {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
     826             :                                 },
     827             :                             },
     828             :                         },
     829             :                     },
     830        4554 :                     {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of\n"
     831             :             "       \"ALL\"\n"
     832             :             "       \"NONE\"\n"
     833             :             "       \"SINGLE\"\n"
     834             :             "       \"ALL|ANYONECANPAY\"\n"
     835             :             "       \"NONE|ANYONECANPAY\"\n"
     836             :             "       \"SINGLE|ANYONECANPAY\""},
     837             :                 },
     838        4554 :                 RPCResult{
     839        4554 :                     RPCResult::Type::OBJ, "", "",
     840       18216 :                     {
     841        4554 :                         {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
     842        4554 :                         {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
     843        9108 :                         {RPCResult::Type::ARR, "errors", /*optional=*/true, "Script verification errors (if there are any)",
     844        9108 :                         {
     845        9108 :                             {RPCResult::Type::OBJ, "", "",
     846       27324 :                             {
     847        4554 :                                 {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
     848        4554 :                                 {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
     849        4554 :                                 {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
     850        4554 :                                 {RPCResult::Type::NUM, "sequence", "Script sequence number"},
     851        4554 :                                 {RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
     852             :                             }},
     853             :                         }},
     854             :                     }
     855             :                 },
     856        4554 :                 RPCExamples{
     857        4554 :                     HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
     858        4554 :             + HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")
     859             :                 },
     860        6212 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     861             : {
     862        1674 :     RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
     863             : 
     864        1658 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
     865        1658 :     if (!pwallet) return UniValue::VNULL;
     866             : 
     867        1658 :     CMutableTransaction mtx;
     868        1658 :     if (!DecodeHexTx(mtx, request.params[0].get_str())) {
     869           0 :         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
     870             :     }
     871             : 
     872             :     // Sign the transaction
     873        1658 :     LOCK(pwallet->cs_wallet);
     874        1658 :     EnsureWalletIsUnlocked(*pwallet);
     875             : 
     876             :     // Fetch previous transactions (inputs):
     877        1654 :     std::map<COutPoint, Coin> coins;
     878        4176 :     for (const CTxIn& txin : mtx.vin) {
     879        2522 :         coins[txin.prevout]; // Create empty map entry keyed by prevout.
     880             :     }
     881        1654 :     pwallet->chain().findCoins(coins);
     882             : 
     883             :     // Parse the prevtxs array
     884        1654 :     ParsePrevouts(request.params[1], nullptr, coins);
     885             : 
     886        1642 :     int nHashType = ParseSighashString(request.params[2]);
     887             : 
     888             :     // Script verification errors
     889        1642 :     std::map<int, bilingual_str> input_errors;
     890             : 
     891        1642 :     bool complete = pwallet->SignTransaction(mtx, coins, nHashType, input_errors);
     892        1642 :     UniValue result(UniValue::VOBJ);
     893        1642 :     SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
     894        1642 :     return result;
     895        1658 : },
     896             :     };
     897           0 : }
     898             : 
     899        3426 : RPCHelpMan send()
     900             : {
     901        6852 :     return RPCHelpMan{"send",
     902        3426 :         "\nEXPERIMENTAL warning: this call may be changed in future releases.\n"
     903             :         "\nSend a transaction.\n",
     904       20556 :         {
     905        6852 :             {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs specified as key-value pairs.\n"
     906             :                     "Each key may only appear once, i.e. there can only be one 'data' output, and no address may be duplicated.\n"
     907             :                     "At least one output of either type must be specified.\n"
     908             :                     "For convenience, a dictionary, which holds the key-value pairs directly, is also accepted.",
     909       10278 :                 {
     910        6852 :                     {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
     911        6852 :                         {
     912        3426 :                             {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the Dash address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
     913             :                         },
     914             :                         },
     915        6852 :                     {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
     916        6852 :                         {
     917        3426 :                             {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
     918             :                         },
     919             :                     },
     920             :                 },
     921             :             },
     922        3426 :             {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
     923        6852 :             {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
     924        3426 :                         "       \"" + FeeModes("\"\n\"") + "\""},
     925        3426 :             {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     926        6852 :             {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
     927        3426 :                 Cat<std::vector<RPCArg>>(
     928       44538 :                 {
     929        3426 :                     {"add_inputs", RPCArg::Type::BOOL, RPCArg::DefaultHint{"false when \"inputs\" are specified, true otherwise"},"Automatically include coins from the wallet to cover the target amount.\n"},
     930        3426 :                     {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
     931             :                                                           "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
     932             :                                                           "If that happens, you will need to fund the transaction with different inputs and republish it."},
     933        3426 :                     {"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns a serialized transaction which will not be added to the wallet or broadcast"},
     934        3426 :                     {"change_address", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The Dash address to receive the change"},
     935        3426 :                     {"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
     936        3426 :                     {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     937        3426 :                     {"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
     938             :                                           "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
     939             :                                           "e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
     940        6852 :                     {"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Specify inputs instead of adding them automatically. A JSON array of JSON objects",
     941       17130 :                         {
     942        3426 :                             {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
     943        3426 :                             {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
     944        3426 :                             {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
     945        3426 :                             {"size", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum size for this input, "
     946             :                                         "including the size of the outpoint and sequence number. "
     947             :                                         "Note that signature sizes are not guaranteed to be consistent, "
     948             :                                         "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."},
     949             :                         },
     950             :                     },
     951        3426 :                     {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
     952        3426 :                     {"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
     953        3426 :                     {"psbt", RPCArg::Type::BOOL,  RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
     954        6852 :                     {"subtract_fee_from_outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Outputs to subtract the fee from, specified as integer indices.\n"
     955             :                     "The fee will be equally deducted from the amount of each specified output.\n"
     956             :                     "Those recipients will receive less funds than you enter in their corresponding amount field.\n"
     957             :                     "If no outputs are specified here, the sender pays the fee.",
     958        6852 :                         {
     959        3426 :                             {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
     960             :                         },
     961             :                     },
     962             :                 },
     963        3426 :                 FundTxDoc()),
     964        3426 :                 "options"},
     965             :         },
     966        3426 :         RPCResult{
     967        3426 :             RPCResult::Type::OBJ, "", "",
     968       17130 :                 {
     969        3426 :                     {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
     970        3426 :                     {RPCResult::Type::STR_HEX, "txid", /*optional=*/true, "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
     971        3426 :                     {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
     972        3426 :                     {RPCResult::Type::STR, "psbt", /*optional=*/true, "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
     973             :                 }
     974             :         },
     975        3426 :         RPCExamples{""
     976             :         "\nSend 0.1 Dash with a confirmation target of 6 blocks in economical fee estimate mode\n"
     977        3426 :         + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 6 economical\n") +
     978        3426 :         "Send 0.2 Dash with a fee rate of 1.1 " + CURRENCY_ATOM + "/B using positional arguments\n"
     979        3426 :         + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" 1.1\n") +
     980        3426 :         "Send 0.2 Dash with a fee rate of 1 " + CURRENCY_ATOM + "/B using the options argument\n"
     981        3426 :         + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" null '{\"fee_rate\": 1}'\n") +
     982        3426 :         "Send 0.3 Dash with a fee rate of 25 " + CURRENCY_ATOM + "/B using named arguments\n"
     983        3426 :         + HelpExampleCli("-named send", "outputs='{\"" + EXAMPLE_ADDRESS[0] + "\": 0.3}' fee_rate=25\n") +
     984             :         "Create a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n"
     985        3426 :         + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 economical '{\"add_to_wallet\": false, \"inputs\": [{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\", \"vout\":1}]}'")
     986             :         },
     987        3956 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     988             :         {
     989        3535 :             RPCTypeCheck(request.params, {
     990         530 :                 UniValueType(), // outputs (ARR or OBJ, checked later)
     991         530 :                 UniValue::VNUM, // conf_target
     992         530 :                 UniValue::VSTR, // estimate_mode
     993         530 :                 UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
     994         530 :                 UniValue::VOBJ, // options
     995             :                 }, true
     996             :             );
     997             : 
     998         530 :             std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     999         530 :             if (!pwallet) return UniValue::VNULL;
    1000             : 
    1001         530 :             UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
    1002         530 :             InterpretFeeEstimationInstructions(/*conf_target=*/request.params[1], /*estimate_mode=*/request.params[2], /*fee_rate=*/request.params[3], options);
    1003         514 :             PreventOutdatedOptions(options);
    1004             : 
    1005             : 
    1006             :             CAmount fee;
    1007             :             int change_position;
    1008         510 :             CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"]);
    1009         506 :             CCoinControl coin_control;
    1010             :             // Automatically select coins, unless at least one is manually selected. Can
    1011             :             // be overridden by options.add_inputs.
    1012         506 :             coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
    1013         506 :             SetOptionsInputWeights(options["inputs"], options);
    1014         502 :             FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
    1015             : 
    1016         175 :             return FinishTransaction(pwallet, options, rawTx);
    1017         534 :         }
    1018             :     };
    1019           0 : }
    1020             : 
    1021        3072 : RPCHelpMan sendall()
    1022             : {
    1023        6144 :     return RPCHelpMan{"sendall",
    1024        3072 :         "EXPERIMENTAL warning: this call may be changed in future releases.\n"
    1025             :         "\nSpend the value of all (or specific) confirmed UTXOs in the wallet to one or more recipients.\n"
    1026             :         "Unconfirmed inbound UTXOs and locked UTXOs will not be spent. Sendall will respect the avoid_reuse wallet flag.\n"
    1027             :         "If your wallet contains many small inputs, either because it received tiny payments or as a result of accumulating change, consider using `send_max` to exclude inputs that are worth less than the fees needed to spend them.\n",
    1028       18432 :         {
    1029        6144 :             {"recipients", RPCArg::Type::ARR, RPCArg::Optional::NO, "The sendall destinations. Each address may only appear once.\n"
    1030             :                 "Optionally some recipients can be specified with an amount to perform payments, but at least one address must appear without a specified amount.\n",
    1031        9216 :                 {
    1032        3072 :                     {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "A Dash address which receives an equal share of the unspecified amount."},
    1033        6144 :                     {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
    1034        6144 :                         {
    1035        3072 :                             {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the Dash address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
    1036             :                         },
    1037             :                     },
    1038             :                 },
    1039             :             },
    1040        3072 :             {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
    1041        6144 :             {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
    1042        3072 :                         "       \"" + FeeModes("\"\n\"") + "\""},
    1043        3072 :             {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
    1044        3072 :             {
    1045        3072 :                 "options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
    1046        3072 :                 Cat<std::vector<RPCArg>>(
    1047       27648 :                     {
    1048        3072 :                         {"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns the serialized transaction without broadcasting or adding it to the wallet"},
    1049        3072 :                         {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
    1050        3072 :                         {"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch-only.\n"
    1051             :                                               "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
    1052             :                                               "e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
    1053        6144 :                         {"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Use exactly the specified inputs to build the transaction. Specifying inputs is incompatible with send_max.",
    1054        6144 :                             {
    1055        6144 :                                 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
    1056       12288 :                                     {
    1057        3072 :                                         {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
    1058        3072 :                                         {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
    1059        3072 :                                         {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' argument"}, "The sequence number"},
    1060             :                                     },
    1061             :                                 },
    1062             :                             },
    1063             :                         },
    1064        3072 :                         {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
    1065        3072 :                         {"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
    1066        3072 :                         {"psbt", RPCArg::Type::BOOL,  RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
    1067        3072 :                         {"send_max", RPCArg::Type::BOOL, RPCArg::Default{false}, "When true, only use UTXOs that can pay for their own fees to maximize the output amount. When 'false' (default), no UTXO is left behind. send_max is incompatible with providing specific inputs."},
    1068             :                     },
    1069        3072 :                     FundTxDoc()
    1070             :                 ),
    1071        3072 :                 "options"
    1072             :             },
    1073             :         },
    1074        3072 :         RPCResult{
    1075        3072 :             RPCResult::Type::OBJ, "", "",
    1076       15360 :                 {
    1077        3072 :                     {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
    1078        3072 :                     {RPCResult::Type::STR_HEX, "txid", /*optional=*/true, "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
    1079        3072 :                     {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
    1080        3072 :                     {RPCResult::Type::STR, "psbt", /*optional=*/true, "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
    1081             :                 }
    1082             :         },
    1083        3072 :         RPCExamples{""
    1084        3072 :         "\nSpend all UTXOs from the wallet with a fee rate of 1 " + CURRENCY_ATOM + "/B using named arguments\n"
    1085        3072 :         + HelpExampleCli("-named sendall", "recipients='[\"" + EXAMPLE_ADDRESS[0] + "\"]' fee_rate=1\n") +
    1086        3072 :         "Spend all UTXOs with a fee rate of 1.1 " + CURRENCY_ATOM + "/B using positional arguments\n"
    1087        3072 :         + HelpExampleCli("sendall", "'[\"" + EXAMPLE_ADDRESS[0] + "\"]' null \"unset\" 1.1\n") +
    1088        3072 :         "Spend all UTXOs split into equal amounts to two addresses with a fee rate of 1.5 " + CURRENCY_ATOM + "/B using the options argument\n"
    1089        3072 :         + HelpExampleCli("sendall", "'[\"" + EXAMPLE_ADDRESS[0] + "\", \"" + EXAMPLE_ADDRESS[1] + "\"]' null \"unset\" null '{\"fee_rate\": 1.5}'\n") +
    1090        3072 :         "Leave dust UTXOs in wallet, spend only UTXOs with positive effective value with a fee rate of 10 " + CURRENCY_ATOM + "/B using the options argument\n"
    1091        3072 :         + HelpExampleCli("sendall", "'[\"" + EXAMPLE_ADDRESS[0] + "\"]' null \"unset\" null '{\"fee_rate\": 10, \"send_max\": true}'\n") +
    1092        3072 :         "Spend all UTXOs with a fee rate of 1.3 " + CURRENCY_ATOM + "/B using named arguments and sending a 0.25 " + CURRENCY_UNIT + " to another recipient\n"
    1093        3072 :         + HelpExampleCli("-named sendall", "recipients='[{\"" + EXAMPLE_ADDRESS[1] + "\": 0.25}, \""+ EXAMPLE_ADDRESS[0] + "\"]' fee_rate=1.3\n")
    1094             :         },
    1095        3250 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1096             :         {
    1097        1136 :             RPCTypeCheck(request.params, {
    1098         178 :                 UniValue::VARR, // recipients
    1099         178 :                 UniValue::VNUM, // conf_target
    1100         178 :                 UniValue::VSTR, // estimate_mode
    1101         178 :                 UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
    1102         178 :                 UniValue::VOBJ, // options
    1103             :                 }, true
    1104             :             );
    1105             : 
    1106         178 :             std::shared_ptr<CWallet> const pwallet{GetWalletForJSONRPCRequest(request)};
    1107         178 :             if (!pwallet) return UniValue::VNULL;
    1108             :             // Make sure the results are valid at least up to the most recent block
    1109             :             // the user could have gotten from another RPC command prior to now
    1110         178 :             pwallet->BlockUntilSyncedToCurrentChain();
    1111             : 
    1112         176 :             UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
    1113         178 :             InterpretFeeEstimationInstructions(/*conf_target=*/request.params[1], /*estimate_mode=*/request.params[2], /*fee_rate=*/request.params[3], options);
    1114         176 :             PreventOutdatedOptions(options);
    1115             : 
    1116             : 
    1117         178 :             std::set<std::string> addresses_without_amount;
    1118         178 :             UniValue recipient_key_value_pairs(UniValue::VARR);
    1119         178 :             const UniValue& recipients{request.params[0]};
    1120         392 :             for (unsigned int i = 0; i < recipients.size(); ++i) {
    1121         216 :                 const UniValue& recipient{recipients[i]};
    1122         216 :                 if (recipient.isStr()) {
    1123         182 :                     UniValue rkvp(UniValue::VOBJ);
    1124         182 :                     rkvp.pushKV(recipient.get_str(), 0);
    1125         182 :                     recipient_key_value_pairs.push_back(rkvp);
    1126         182 :                     addresses_without_amount.insert(recipient.get_str());
    1127         182 :                 } else {
    1128          34 :                     recipient_key_value_pairs.push_back(recipient);
    1129             :                 }
    1130         216 :             }
    1131             : 
    1132         176 :             if (addresses_without_amount.size() == 0) {
    1133           8 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Must provide at least one address without a specified amount");
    1134             :             }
    1135             : 
    1136         168 :             CCoinControl coin_control;
    1137             : 
    1138         170 :             SetFeeEstimateMode(*pwallet, coin_control, options["conf_target"], options["estimate_mode"], options["fee_rate"], /*override_min_fee=*/false);
    1139             : 
    1140         168 :             coin_control.fAllowWatchOnly = ParseIncludeWatchonly(options["include_watching"], *pwallet);
    1141             : 
    1142         168 :             FeeCalculation fee_calc_out;
    1143         168 :             CFeeRate fee_rate{GetMinimumFeeRate(*pwallet, coin_control, &fee_calc_out)};
    1144             :             // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
    1145             :             // provided one
    1146         168 :             if (coin_control.m_feerate && fee_rate > *coin_control.m_feerate) {
    1147           4 :                throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee rate (%s) is lower than the minimum fee rate setting (%s)", coin_control.m_feerate->ToString(FeeEstimateMode::DUFF_B), fee_rate.ToString(FeeEstimateMode::DUFF_B)));
    1148             :             }
    1149         164 :             if (fee_calc_out.reason == FeeReason::FALLBACK && !pwallet->m_allow_fallback_fee) {
    1150             :                 // eventually allow a fallback fee
    1151           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
    1152             :             }
    1153             : 
    1154         164 :             CMutableTransaction rawTx{ConstructTransaction(options["inputs"], recipient_key_value_pairs, options["locktime"])};
    1155         166 :             LOCK(pwallet->cs_wallet);
    1156             : 
    1157         166 :             CAmount total_input_value(0);
    1158         166 :             bool send_max{options.exists("send_max") ? options["send_max"].get_bool() : false};
    1159         190 :             if (options.exists("inputs") && options.exists("send_max")) {
    1160           4 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot combine send_max with specific inputs.");
    1161         156 :             } else if (options.exists("inputs")) {
    1162          36 :                 for (const CTxIn& input : rawTx.vin) {
    1163          26 :                     if (pwallet->IsSpent(input.prevout)) {
    1164           8 :                         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) was already spent.", input.prevout.hash.ToString(), input.prevout.n));
    1165             :                     }
    1166          18 :                     const CWalletTx* tx{pwallet->GetWalletTx(input.prevout.hash)};
    1167          21 :                     if (!tx || input.prevout.n >= tx->tx->vout.size() || !(pwallet->IsMine(tx->tx->vout[input.prevout.n]) & (coin_control.fAllowWatchOnly ? ISMINE_ALL : ISMINE_SPENDABLE))) {
    1168           8 :                         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not found. UTXO (%s:%d) is not part of wallet.", input.prevout.hash.ToString(), input.prevout.n));
    1169             :                     }
    1170          10 :                     total_input_value += tx->tx->vout[input.prevout.n].nValue;
    1171             :                 }
    1172          10 :             } else {
    1173        6910 :                 for (const COutput& output : AvailableCoins(*pwallet, &coin_control, fee_rate, /*nMinimumAmount=*/0).all()) {
    1174        6780 :                     CHECK_NONFATAL(output.input_bytes > 0);
    1175        6780 :                     if (send_max && fee_rate.GetFee(output.input_bytes) > output.txout.nValue) {
    1176           8 :                         continue;
    1177             :                     }
    1178        6772 :                     CTxIn input(output.outpoint.hash, output.outpoint.n, CScript(), CTxIn::SEQUENCE_FINAL);
    1179        6772 :                     rawTx.vin.push_back(input);
    1180        6772 :                     total_input_value += output.txout.nValue;
    1181        6772 :                 }
    1182             :             }
    1183             : 
    1184             :             // estimate final size of tx
    1185         140 :             const int64_t tx_size{CalculateMaximumSignedTxSize(CTransaction(rawTx), pwallet.get())};
    1186         140 :             const CAmount fee_from_size{fee_rate.GetFee(tx_size)};
    1187         140 :             const CAmount effective_value{total_input_value - fee_from_size};
    1188             : 
    1189         140 :             if (fee_from_size > pwallet->m_default_max_tx_fee) {
    1190           4 :                 throw JSONRPCError(RPC_WALLET_ERROR, TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED).original);
    1191             :             }
    1192             : 
    1193         136 :             if (effective_value <= 0) {
    1194           4 :                 if (send_max) {
    1195           0 :                     throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Total value of UTXO pool too low to pay for transaction, try using lower feerate.");
    1196             :                 } else {
    1197           4 :                     throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Total value of UTXO pool too low to pay for transaction. Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.");
    1198             :                 }
    1199             :             }
    1200             : 
    1201             :             // If this transaction is too large, e.g. because the wallet has many UTXOs, it will be rejected by the node's mempool.
    1202         132 :             if (tx_size > MAX_STANDARD_TX_SIZE) {
    1203           4 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction too large.");
    1204             :             }
    1205             : 
    1206         128 :             CAmount output_amounts_claimed{0};
    1207         296 :             for (CTxOut out : rawTx.vout) {
    1208         168 :                 output_amounts_claimed += out.nValue;
    1209         168 :             }
    1210             : 
    1211         128 :             if (output_amounts_claimed > total_input_value) {
    1212           4 :                 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Assigned more value to outputs than available funds.");
    1213             :             }
    1214             : 
    1215         124 :             const CAmount remainder{effective_value - output_amounts_claimed};
    1216         124 :             if (remainder < 0) {
    1217           4 :                 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds for fees after creating specified outputs.");
    1218             :             }
    1219             : 
    1220         120 :             const CAmount per_output_without_amount{remainder / (long)addresses_without_amount.size()};
    1221             : 
    1222         120 :             bool gave_remaining_to_first{false};
    1223         256 :             for (CTxOut& out : rawTx.vout) {
    1224         148 :                 CTxDestination dest;
    1225         148 :                 ExtractDestination(out.scriptPubKey, dest);
    1226         148 :                 std::string addr{EncodeDestination(dest)};
    1227         148 :                 if (addresses_without_amount.count(addr) > 0) {
    1228         126 :                     out.nValue = per_output_without_amount;
    1229         126 :                     if (!gave_remaining_to_first) {
    1230         116 :                         out.nValue += remainder % addresses_without_amount.size();
    1231         116 :                         gave_remaining_to_first = true;
    1232         116 :                     }
    1233         126 :                     if (IsDust(out, pwallet->chain().relayDustFee())) {
    1234             :                         // Dynamically generated output amount is dust
    1235           8 :                         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Dynamically assigned remainder results in dust output.");
    1236             :                     }
    1237         118 :                 } else {
    1238          22 :                     if (IsDust(out, pwallet->chain().relayDustFee())) {
    1239             :                         // Specified output amount is dust
    1240           4 :                         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Specified output amount to %s is below dust threshold.", addr));
    1241             :                     }
    1242             :                 }
    1243         148 :             }
    1244             : 
    1245         108 :             const bool lock_unspents{options.exists("lock_unspents") ? options["lock_unspents"].get_bool() : false};
    1246         108 :             if (lock_unspents) {
    1247           0 :                 for (const CTxIn& txin : rawTx.vin) {
    1248           0 :                     pwallet->LockCoin(txin.prevout);
    1249             :                 }
    1250           0 :             }
    1251             : 
    1252         108 :             return FinishTransaction(pwallet, options, rawTx);
    1253         276 :         }
    1254             :     };
    1255           0 : }
    1256             : 
    1257        3077 : RPCHelpMan walletprocesspsbt()
    1258             : {
    1259        6154 :     return RPCHelpMan{"walletprocesspsbt",
    1260             :                 "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
    1261        3077 :                 "that we can sign for." +
    1262             :         HELP_REQUIRING_PASSPHRASE,
    1263       18462 :                 {
    1264        3077 :                     {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
    1265        3077 :                     {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating (requires wallet to be unlocked)"},
    1266        3077 :                     {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
    1267             :             "       \"ALL\"\n"
    1268             :             "       \"NONE\"\n"
    1269             :             "       \"SINGLE\"\n"
    1270             :             "       \"ALL|ANYONECANPAY\"\n"
    1271             :             "       \"NONE|ANYONECANPAY\"\n"
    1272             :             "       \"SINGLE|ANYONECANPAY\""},
    1273        3077 :                     {"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
    1274        3077 :                     {"finalize", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also finalize inputs if possible"},
    1275             :                 },
    1276        3077 :                 RPCResult{
    1277        3077 :                     RPCResult::Type::OBJ, "", "",
    1278        9231 :                     {
    1279        3077 :                         {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"},
    1280        3077 :                         {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
    1281             :                     }
    1282             :                 },
    1283        3077 :                 RPCExamples{
    1284        3077 :                     HelpExampleCli("walletprocesspsbt", "\"psbt\"")
    1285             :                 },
    1286        3258 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1287             : {
    1288         193 :     RPCTypeCheck(request.params, {UniValue::VSTR});
    1289             : 
    1290         181 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
    1291         181 :     if (!pwallet) return UniValue::VNULL;
    1292             : 
    1293         181 :     const CWallet& wallet{*pwallet};
    1294             :     // Make sure the results are valid at least up to the most recent block
    1295             :     // the user could have gotten from another RPC command prior to now
    1296         181 :     wallet.BlockUntilSyncedToCurrentChain();
    1297             : 
    1298             :     // Unserialize the transaction
    1299         181 :     PartiallySignedTransaction psbtx;
    1300         181 :     std::string error;
    1301         181 :     if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
    1302           4 :         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
    1303             :     }
    1304             : 
    1305             :     // Get the sighash type
    1306         177 :     int nHashType = ParseSighashString(request.params[2]);
    1307             : 
    1308             :     // Use CTransaction for the constant parts of the
    1309             :     // transaction to avoid rehashing.
    1310         177 :     const CTransaction txConst(*psbtx.tx);
    1311             : 
    1312             :     // Fill transaction with our data and also sign
    1313         177 :     bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
    1314         177 :     bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
    1315         177 :     bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
    1316         177 :     bool complete = true;
    1317             : 
    1318         177 :     if (sign) EnsureWalletIsUnlocked(*pwallet);
    1319             : 
    1320         173 :     const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs, nullptr, finalize)};
    1321         173 :     if (err != TransactionError::OK) {
    1322           4 :         throw JSONRPCTransactionError(err);
    1323             :     }
    1324             : 
    1325         169 :     UniValue result(UniValue::VOBJ);
    1326         169 :     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    1327         169 :     ssTx << psbtx;
    1328         169 :     result.pushKV("psbt", EncodeBase64(ssTx.str()));
    1329         169 :     result.pushKV("complete", complete);
    1330             : 
    1331         169 :     return result;
    1332         189 : },
    1333             :     };
    1334           0 : }
    1335             : 
    1336        3368 : RPCHelpMan walletcreatefundedpsbt()
    1337             : {
    1338        6736 :     return RPCHelpMan{"walletcreatefundedpsbt",
    1339        3368 :                 "\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
    1340             :                 "Implements the Creator and Updater roles.\n"
    1341             :                 "All existing inputs must either have their previous output transaction be in the wallet\n"
    1342             :                 "or be in the UTXO set. Solving data must be provided for non-wallet inputs.\n",
    1343       20208 :                 {
    1344        6736 :                     {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Leave empty to add inputs automatically. See add_inputs option.",
    1345        6736 :                         {
    1346        6736 :                             {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
    1347       16840 :                                 {
    1348        3368 :                                     {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
    1349        3368 :                                     {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
    1350        3368 :                                     {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' argument"}, "The sequence number"},
    1351        3368 :                                     {"size", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum size for this input, "
    1352             :                                         "including the size of the outpoint and sequence number. "
    1353             :                                         "Note that signature sizes are not guaranteed to be consistent, "
    1354             :                                         "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."},
    1355             :                                 },
    1356             :                             },
    1357             :                         },
    1358             :                         },
    1359        6736 :                     {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
    1360             :                             "That is, each address can only appear once and there can only be one 'data' object.\n"
    1361             :                             "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
    1362             :                             "accepted as second parameter.",
    1363       10104 :                         {
    1364        6736 :                             {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
    1365        6736 :                                 {
    1366        3368 :                                     {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the Dash address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
    1367             :                                 },
    1368             :                                 },
    1369        6736 :                             {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
    1370        6736 :                                 {
    1371        3368 :                                     {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
    1372             :                                 },
    1373             :                             },
    1374             :                         },
    1375             :                     },
    1376        3368 :                     {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
    1377        6736 :                     {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
    1378        3368 :                         Cat<std::vector<RPCArg>>(
    1379       33680 :                         {
    1380        3368 :                             {"add_inputs", RPCArg::Type::BOOL, RPCArg::DefaultHint{"false when \"inputs\" are specified, true otherwise"}, "Automatically include coins from the wallet to cover the target amount.\n"},
    1381        3368 :                             {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
    1382             :                                                           "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
    1383             :                                                           "If that happens, you will need to fund the transaction with different inputs and republish it."},
    1384        3368 :                             {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The Dash address to receive the change"},
    1385        3368 :                             {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
    1386        3368 :                             {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"},
    1387        3368 :                             {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
    1388        3368 :                             {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
    1389        3368 :                             {"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set: makes wallet determine the fee"}, "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
    1390        6736 :                             {"subtractFeeFromOutputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The outputs to subtract the fee from.\n"
    1391             :                                                           "The fee will be equally deducted from the amount of each specified output.\n"
    1392             :                                                           "Those recipients will receive less Dash than you enter in their corresponding amount field.\n"
    1393             :                                                           "If no outputs are specified here, the sender pays the fee.",
    1394        6736 :                                 {
    1395        3368 :                                     {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
    1396             :                                 },
    1397             :                             },
    1398             :                         },
    1399        3368 :                         FundTxDoc()),
    1400        3368 :                         "options"},
    1401        3368 :                     {"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
    1402             :                 },
    1403        3368 :                 RPCResult{
    1404        3368 :                     RPCResult::Type::OBJ, "", "",
    1405       13472 :                     {
    1406        3368 :                         {RPCResult::Type::STR, "psbt", "The resulting raw transaction (base64-encoded string)"},
    1407        3368 :                         {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
    1408        3368 :                         {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
    1409             :                     }
    1410             :                                 },
    1411        3368 :                                 RPCExamples{
    1412             :                             "\nCreate a transaction with no inputs\n"
    1413        3368 :                             + HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
    1414             :                                 },
    1415        3840 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1416             : {
    1417        3089 :     RPCTypeCheck(request.params, {
    1418         472 :         UniValue::VARR,
    1419         472 :         UniValueType(), // ARR or OBJ, checked later
    1420         472 :         UniValue::VNUM,
    1421         472 :         UniValue::VOBJ,
    1422         472 :         UniValue::VBOOL,
    1423             :         }, true
    1424             :     );
    1425             : 
    1426         472 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
    1427         472 :     if (!pwallet) return UniValue::VNULL;
    1428             : 
    1429         472 :     CWallet& wallet{*pwallet};
    1430             :     // Make sure the results are valid at least up to the most recent block
    1431             :     // the user could have gotten from another RPC command prior to now
    1432         472 :     wallet.BlockUntilSyncedToCurrentChain();
    1433             : 
    1434         472 :     UniValue options{request.params[3].isNull() ? UniValue::VOBJ : request.params[3]};
    1435             : 
    1436             :     CAmount fee;
    1437             :     int change_position;
    1438         472 :     CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2]);
    1439         472 :     CCoinControl coin_control;
    1440             :     // Automatically select coins, unless at least one is manually selected. Can
    1441             :     // be overridden by options.add_inputs.
    1442         472 :     coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
    1443         472 :     SetOptionsInputWeights(request.params[0], options);
    1444         468 :     FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /*override_min_fee=*/true);
    1445             : 
    1446             :     // Make a blank psbt
    1447         215 :     PartiallySignedTransaction psbtx{rawTx};
    1448             : 
    1449             :     // Fill transaction with out data but don't sign
    1450         215 :     bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
    1451         215 :     bool complete = true;
    1452         215 :     const TransactionError err{wallet.FillPSBT(psbtx, complete, 1, false, bip32derivs)};
    1453         215 :     if (err != TransactionError::OK) {
    1454           0 :         throw JSONRPCTransactionError(err);
    1455             :     }
    1456             : 
    1457             :     // Serialize the PSBT
    1458         215 :     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    1459         215 :     ssTx << psbtx;
    1460             : 
    1461         215 :     UniValue result(UniValue::VOBJ);
    1462         215 :     result.pushKV("psbt", EncodeBase64(ssTx.str()));
    1463         215 :     result.pushKV("fee", ValueFromAmount(fee));
    1464         215 :     result.pushKV("changepos", change_position);
    1465         215 :     return result;
    1466         472 : },
    1467             :     };
    1468           0 : }
    1469             : } // namespace wallet

Generated by: LCOV version 1.16