LCOV - code coverage report
Current view: top level - src/wallet/rpc - spend.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 350 921 38.0 %
Date: 2026-06-25 07:23:51 Functions: 13 30 43.3 %

          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           0 : static void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient>& recipients)
      26             : {
      27           0 :     std::set<CTxDestination> destinations;
      28           0 :     int i = 0;
      29           0 :     for (const std::string& address: address_amounts.getKeys()) {
      30           0 :         CTxDestination dest = DecodeDestination(address);
      31           0 :         if (!IsValidDestination(dest)) {
      32           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + address);
      33             :         }
      34             : 
      35           0 :         if (destinations.count(dest)) {
      36           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + address);
      37             :         }
      38           0 :         destinations.insert(dest);
      39             : 
      40           0 :         CScript script_pub_key = GetScriptForDestination(dest);
      41           0 :         CAmount amount = AmountFromValue(address_amounts[i++]);
      42             : 
      43           0 :         bool subtract_fee = false;
      44           0 :         for (unsigned int idx = 0; idx < subtract_fee_outputs.size(); idx++) {
      45           0 :             const UniValue& addr = subtract_fee_outputs[idx];
      46           0 :             if (addr.get_str() == address) {
      47           0 :                 subtract_fee = true;
      48           0 :             }
      49           0 :         }
      50             : 
      51           0 :         CRecipient recipient = {script_pub_key, amount, subtract_fee};
      52           0 :         recipients.push_back(recipient);
      53           0 :     }
      54           0 : }
      55             : 
      56           0 : static void InterpretFeeEstimationInstructions(const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, UniValue& options)
      57             : {
      58           0 :     if (options.exists("conf_target") || options.exists("estimate_mode")) {
      59           0 :         if (!conf_target.isNull() || !estimate_mode.isNull()) {
      60           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Pass conf_target and estimate_mode either as arguments or in the options object, but not both");
      61             :         }
      62           0 :     } else {
      63           0 :         options.pushKV("conf_target", conf_target);
      64           0 :         options.pushKV("estimate_mode", estimate_mode);
      65             :     }
      66           0 :     if (options.exists("fee_rate")) {
      67           0 :         if (!fee_rate.isNull()) {
      68           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Pass the fee_rate either as an argument, or in the options object, but not both");
      69             :         }
      70           0 :     } else {
      71           0 :         options.pushKV("fee_rate", fee_rate);
      72             :     }
      73           0 :     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           0 : }
      77             : 
      78           0 : static UniValue FinishTransaction(const std::shared_ptr<CWallet> pwallet, const UniValue& options, const CMutableTransaction& rawTx)
      79             : {
      80             :     // Make a blank psbt
      81           0 :     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           0 :     (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true);
      87           0 :     const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
      88           0 :     if (err != TransactionError::OK) {
      89           0 :         throw JSONRPCTransactionError(err);
      90             :     }
      91             : 
      92           0 :     CMutableTransaction mtx;
      93           0 :     complete = FinalizeAndExtractPSBT(psbtx, mtx);
      94             : 
      95           0 :     UniValue result(UniValue::VOBJ);
      96             : 
      97           0 :     const bool psbt_opt_in{options.exists("psbt") && options["psbt"].get_bool()};
      98           0 :     bool add_to_wallet{options.exists("add_to_wallet") ? options["add_to_wallet"].get_bool() : true};
      99           0 :     if (psbt_opt_in || !complete || !add_to_wallet) {
     100             :         // Serialize the PSBT
     101           0 :         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
     102           0 :         ssTx << psbtx;
     103           0 :         result.pushKV("psbt", EncodeBase64(ssTx.str()));
     104           0 :     }
     105             : 
     106           0 :     if (complete) {
     107           0 :         std::string hex{EncodeHexTx(CTransaction(mtx))};
     108           0 :         CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
     109           0 :         result.pushKV("txid", tx->GetHash().GetHex());
     110           0 :         if (add_to_wallet && !psbt_opt_in) {
     111           0 :             pwallet->CommitTransaction(tx, {}, /*orderForm*/ {});
     112           0 :         } else {
     113           0 :             result.pushKV("hex", hex);
     114             :         }
     115           0 :     }
     116           0 :     result.pushKV("complete", complete);
     117             : 
     118           0 :     return result;
     119           0 : }
     120             : 
     121           0 : static void PreventOutdatedOptions(const UniValue& options)
     122             : {
     123           0 :     if (options.exists("feeRate")) {
     124           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use fee_rate (" + CURRENCY_ATOM + "/B) instead of feeRate");
     125             :     }
     126           0 :     if (options.exists("changeAddress")) {
     127           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_address instead of changeAddress");
     128             :     }
     129           0 :     if (options.exists("changePosition")) {
     130           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_position instead of changePosition");
     131             :     }
     132           0 :     if (options.exists("includeWatching")) {
     133           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use include_watching instead of includeWatching");
     134             :     }
     135           0 :     if (options.exists("lockUnspents")) {
     136           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use lock_unspents instead of lockUnspents");
     137             :     }
     138           0 :     if (options.exists("subtractFeeFromOutputs")) {
     139           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Use subtract_fee_from_outputs instead of subtractFeeFromOutputs");
     140             :     }
     141           0 : }
     142             : 
     143           0 : UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
     144             : {
     145           0 :     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           0 :     if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     150           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
     151             :     }
     152             : 
     153           0 :     if (coin_control.IsUsingCoinJoin()) {
     154           0 :         map_value["DS"] = "1";
     155           0 :     }
     156             : 
     157             :     // Send
     158           0 :     auto res = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, coin_control, /*sign=*/true);
     159           0 :     if (!res) {
     160           0 :         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, util::ErrorString(res).original);
     161             :     }
     162           0 :     const CTransactionRef& tx = res->tx;
     163           0 :     wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
     164           0 :     if (verbose) {
     165           0 :         UniValue entry(UniValue::VOBJ);
     166           0 :         entry.pushKV("txid", tx->GetHash().GetHex());
     167           0 :         entry.pushKV("fee_reason", StringForFeeReason(res->fee_calc.reason));
     168           0 :         return entry;
     169           0 :     }
     170           0 :     return tx->GetHash().GetHex();
     171           0 : }
     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           0 : 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           0 :     if (!fee_rate.isNull()) {
     189           0 :         if (!conf_target.isNull()) {
     190           0 :             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           0 :         if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") {
     193           0 :             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           0 :         cc.m_feerate = CFeeRate{AmountFromValue(fee_rate, /*decimals=*/3)};
     197           0 :         if (override_min_fee) cc.fOverrideFeeRate = true;
     198           0 :         return;
     199             :     }
     200           0 :     if (!estimate_mode.isNull() && !FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
     201           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
     202             :     }
     203           0 :     if (!conf_target.isNull()) {
     204           0 :         cc.m_confirm_target = ParseConfirmTarget(conf_target, wallet.chain().estimateMaxBlocks());
     205           0 :     }
     206           0 : }
     207             : 
     208           8 : RPCHelpMan sendtoaddress()
     209             : {
     210          16 :     return RPCHelpMan{"sendtoaddress",
     211           8 :         "\nSend an amount to a given address." +
     212             :         HELP_REQUIRING_PASSPHRASE,
     213         104 :                 {
     214           8 :                     {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Dash address to send to."},
     215           8 :                     {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
     216           8 :                     {"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           8 :                     {"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           8 :                     {"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           8 :                     {"use_is", RPCArg::Type::BOOL, RPCArg::Default{false}, "Deprecated and ignored"},
     224           8 :                     {"use_cj", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use CoinJoin funds only"},
     225           8 :                     {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
     226          16 :                     {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
     227           8 :                     "       \"" + FeeModes("\"\n\"") + "\""},
     228           8 :                     {"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           8 :                     {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     231           8 :                     {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra information about the transaction."},
     232             :                 },
     233          24 :                 {
     234          16 :                     RPCResult{"if verbose is not set or set to false",
     235           8 :                         RPCResult::Type::STR_HEX, "txid", "The transaction id."
     236             :                     },
     237          16 :                     RPCResult{"if verbose is set to true",
     238           8 :                         RPCResult::Type::OBJ, "", "",
     239          24 :                         {
     240           8 :                             {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
     241           8 :                             {RPCResult::Type::STR, "fee_reason", "The transaction fee reason."}
     242             :                         },
     243             :                     },
     244             :                 },
     245           8 :                 RPCExamples{
     246             :                     "\nSend 0.1 Dash\n"
     247           8 :                     + 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           8 :                     + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"sean's outpost\" false false false 6 economical") +
     250           8 :                     "\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           8 :                     + 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           8 :                     + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.2 conf_target=6 estimate_mode=\"economical\"") +
     254           8 :                     "\nSend 0.5 Dash with a fee rate of 25 " + CURRENCY_ATOM + "/B using named arguments\n"
     255           8 :                     + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.5 fee_rate=25")
     256           8 :                     + 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           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     259             : {
     260           0 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     261           0 :     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           0 :     pwallet->BlockUntilSyncedToCurrentChain();
     266             : 
     267           0 :     LOCK(pwallet->cs_wallet);
     268             : 
     269             :     // Wallet comments
     270           0 :     mapValue_t mapValue;
     271           0 :     if (!request.params[2].isNull() && !request.params[2].get_str().empty())
     272           0 :         mapValue["comment"] = request.params[2].get_str();
     273           0 :     if (!request.params[3].isNull() && !request.params[3].get_str().empty())
     274           0 :         mapValue["to"] = request.params[3].get_str();
     275             : 
     276           0 :     bool fSubtractFeeFromAmount = false;
     277           0 :     if (!request.params[4].isNull()) {
     278           0 :         fSubtractFeeFromAmount = request.params[4].get_bool();
     279           0 :     }
     280             : 
     281           0 :     CCoinControl coin_control;
     282             : 
     283           0 :     if (!request.params[6].isNull()) {
     284           0 :         coin_control.UseCoinJoin(request.params[6].get_bool());
     285           0 :     }
     286             : 
     287           0 :     coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(*pwallet, request.params[9]);
     288             :     // We also enable partial spend avoidance if reuse avoidance is set.
     289           0 :     coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
     290             : 
     291           0 :     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           0 :     EnsureWalletIsUnlocked(*pwallet);
     294             : 
     295           0 :     UniValue address_amounts(UniValue::VOBJ);
     296           0 :     const std::string address = request.params[0].get_str();
     297           0 :     address_amounts.pushKV(address, request.params[1]);
     298           0 :     UniValue subtractFeeFromAmount(UniValue::VARR);
     299           0 :     if (fSubtractFeeFromAmount) {
     300           0 :         subtractFeeFromAmount.push_back(address);
     301           0 :     }
     302             : 
     303           0 :     std::vector<CRecipient> recipients;
     304           0 :     ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
     305           0 :     const bool verbose{request.params[11].isNull() ? false : request.params[11].get_bool()};
     306             : 
     307           0 :     return SendMoney(*pwallet, coin_control, recipients, mapValue, verbose);
     308           0 : },
     309             :     };
     310           0 : }
     311             : 
     312           8 : RPCHelpMan sendmany()
     313             : {
     314          16 :     return RPCHelpMan{"sendmany",
     315           8 :                 "\nSend multiple times. Amounts are double-precision floating point numbers." +
     316             :         HELP_REQUIRING_PASSPHRASE,
     317         104 :                 {
     318           8 :                     {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
     319          16 :                     {"amounts", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::NO, "The addresses and amounts",
     320          16 :                         {
     321           8 :                             {"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           8 :                     {"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
     325           8 :                     {"addlocked", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
     326           8 :                     {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
     327          16 :                     {"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          16 :                         {
     332           8 :                             {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
     333             :                         },
     334             :                     },
     335           8 :                     {"use_is", RPCArg::Type::BOOL, RPCArg::Default{false}, "Deprecated and ignored"},
     336           8 :                     {"use_cj", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use CoinJoin funds only"},
     337           8 :                     {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
     338          16 :                     {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
     339           8 :             "       \"" + FeeModes("\"\n\"") + "\""},
     340           8 :                     {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     341           8 :                     {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra information about the transaction."},
     342             :                 },
     343          24 :                 {
     344          16 :                     RPCResult{"if verbose is not set or set to false",
     345           8 :                         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          16 :                     RPCResult{"if verbose is set to true",
     349           8 :                         RPCResult::Type::OBJ, "", "",
     350          24 :                         {
     351           8 :                             {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           8 :                             {RPCResult::Type::STR, "fee_reason", "The transaction fee reason."}
     354             :                         },
     355             :                     },
     356             :                 },
     357           8 :                 RPCExamples{
     358             :             "\nSend two amounts to two different addresses:\n"
     359           8 :             + 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           8 :             + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\" 6 false \"testing\"") +
     362             :             "\nAs a json rpc call\n"
     363           8 :             + HelpExampleRpc("sendmany", "\"\", \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\", 6, false, \"testing\"")
     364             :                 },
     365           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     366             : {
     367           0 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     368           0 :     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           0 :     pwallet->BlockUntilSyncedToCurrentChain();
     373             : 
     374           0 :     LOCK(pwallet->cs_wallet);
     375             : 
     376           0 :     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           0 :     UniValue sendTo = request.params[1].get_obj();
     380           0 :     mapValue_t mapValue;
     381           0 :     if (!request.params[4].isNull() && !request.params[4].get_str().empty())
     382           0 :         mapValue["comment"] = request.params[4].get_str();
     383             : 
     384           0 :     UniValue subtractFeeFromAmount(UniValue::VARR);
     385           0 :     if (!request.params[5].isNull())
     386           0 :         subtractFeeFromAmount = request.params[5].get_array();
     387             : 
     388             :     // request.params[6] ("use_is") is deprecated and not used here
     389             : 
     390           0 :     CCoinControl coin_control;
     391             : 
     392           0 :     if (!request.params[7].isNull()) {
     393           0 :         coin_control.UseCoinJoin(request.params[7].get_bool());
     394           0 :     }
     395             : 
     396           0 :     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           0 :     std::vector<CRecipient> recipients;
     399           0 :     ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
     400           0 :     const bool verbose{request.params[11].isNull() ? false : request.params[11].get_bool()};
     401             : 
     402           0 :     return SendMoney(*pwallet, coin_control, recipients, std::move(mapValue), verbose);
     403           0 : },
     404             :     };
     405           0 : }
     406             : 
     407           8 : RPCHelpMan settxfee()
     408             : {
     409          16 :     return RPCHelpMan{"settxfee",
     410           8 :                 "\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          16 :                 {
     413           8 :                     {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee rate in " + CURRENCY_UNIT + "/kB"},
     414             :                 },
     415           8 :                 RPCResult{
     416           8 :                     RPCResult::Type::BOOL, "", "Returns true if successful"
     417             :                 },
     418           8 :                 RPCExamples{
     419           8 :                     HelpExampleCli("settxfee", "0.00001")
     420           8 :             + HelpExampleRpc("settxfee", "0.00001")
     421             :                 },
     422           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     423             : {
     424           0 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     425           0 :     if (!pwallet) return UniValue::VNULL;
     426             : 
     427           0 :     LOCK(pwallet->cs_wallet);
     428             : 
     429           0 :     CAmount nAmount = AmountFromValue(request.params[0]);
     430           0 :     CFeeRate tx_fee_rate(nAmount, 1000);
     431           0 :     CFeeRate max_tx_fee_rate(pwallet->m_default_max_tx_fee, 1000);
     432           0 :     if (tx_fee_rate == CFeeRate(0)) {
     433             :         // automatic selection
     434           0 :     } 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           0 :     } 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           0 :     } 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           0 :     pwallet->m_pay_tx_fee = tx_fee_rate;
     443           0 :     return true;
     444           0 : },
     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          32 : static std::vector<RPCArg> FundTxDoc(bool solving_data = true)
     450             : {
     451          96 :     std::vector<RPCArg> args = {
     452          32 :         {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
     453          64 :         {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
     454          32 :             "         \"" + FeeModes("\"\n\"") + "\""},
     455             :     };
     456          32 :     if (solving_data) {
     457          64 :         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         128 :             {
     460          32 :                 {
     461          32 :                     "pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Public keys involved in this transaction.",
     462          64 :                     {
     463          32 :                         {"pubkey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A public key"},
     464             :                     }
     465             :                 },
     466          32 :                 {
     467          32 :                     "scripts", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Scripts involved in this transaction.",
     468          64 :                     {
     469          32 :                         {"script", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A script"},
     470             :                     }
     471             :                 },
     472          32 :                 {
     473          32 :                     "descriptors", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Descriptors that provide solving data for this transaction.",
     474          64 :                     {
     475          32 :                         {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A descriptor"},
     476             :                     }
     477             :                 },
     478             :             }
     479             :         });
     480          32 :     }
     481          32 :     return args;
     482          32 : }
     483             : 
     484           0 : 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           0 :     wallet.BlockUntilSyncedToCurrentChain();
     489             : 
     490           0 :     change_position = -1;
     491           0 :     bool lockUnspents = false;
     492           0 :     UniValue subtractFeeFromOutputs;
     493           0 :     std::set<int> setSubtractFeeFromOutputs;
     494             : 
     495           0 :     if (!options.isNull()) {
     496           0 :       if (options.type() == UniValue::VBOOL) {
     497             :         // backward compatibility bool only fallback
     498           0 :         coinControl.fAllowWatchOnly = options.get_bool();
     499           0 :       }
     500             :       else {
     501           0 :         RPCTypeCheckObj(options,
     502           0 :             {
     503           0 :                 {"add_inputs", UniValueType(UniValue::VBOOL)},
     504           0 :                 {"include_unsafe", UniValueType(UniValue::VBOOL)},
     505           0 :                 {"add_to_wallet", UniValueType(UniValue::VBOOL)},
     506           0 :                 {"changeAddress", UniValueType(UniValue::VSTR)},
     507           0 :                 {"change_address", UniValueType(UniValue::VSTR)},
     508           0 :                 {"changePosition", UniValueType(UniValue::VNUM)},
     509           0 :                 {"change_position", UniValueType(UniValue::VNUM)},
     510           0 :                 {"includeWatching", UniValueType(UniValue::VBOOL)},
     511           0 :                 {"include_watching", UniValueType(UniValue::VBOOL)},
     512           0 :                 {"inputs", UniValueType(UniValue::VARR)},
     513           0 :                 {"lockUnspents", UniValueType(UniValue::VBOOL)},
     514           0 :                 {"lock_unspents", UniValueType(UniValue::VBOOL)},
     515           0 :                 {"locktime", UniValueType(UniValue::VNUM)},
     516           0 :                 {"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
     517           0 :                 {"feeRate", UniValueType()}, // will be checked by AmountFromValue() below
     518           0 :                 {"psbt", UniValueType(UniValue::VBOOL)},
     519           0 :                 {"solving_data", UniValueType(UniValue::VOBJ)},
     520           0 :                 {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
     521           0 :                 {"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
     522           0 :                 {"conf_target", UniValueType(UniValue::VNUM)},
     523           0 :                 {"estimate_mode", UniValueType(UniValue::VSTR)},
     524           0 :                 {"input_sizes", UniValueType(UniValue::VARR)},
     525             :             },
     526             :             true, true);
     527             : 
     528           0 :         if (options.exists("add_inputs")) {
     529           0 :             coinControl.m_allow_other_inputs = options["add_inputs"].get_bool();
     530           0 :         }
     531             : 
     532           0 :         if (options.exists("changeAddress") || options.exists("change_address")) {
     533           0 :             const std::string change_address_str = (options.exists("change_address") ? options["change_address"] : options["changeAddress"]).get_str();
     534           0 :             CTxDestination dest = DecodeDestination(change_address_str);
     535             : 
     536           0 :             if (!IsValidDestination(dest)) {
     537           0 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid Dash address");
     538             :             }
     539             : 
     540           0 :             coinControl.destChange = dest;
     541           0 :         }
     542             : 
     543           0 :         if (options.exists("changePosition") || options.exists("change_position")) {
     544           0 :             change_position = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).getInt<int>();
     545           0 :         }
     546             : 
     547           0 :         const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
     548           0 :         coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, wallet);
     549             : 
     550           0 :         if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
     551           0 :             lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
     552           0 :         }
     553             : 
     554           0 :         if (options.exists("include_unsafe")) {
     555           0 :             coinControl.m_include_unsafe_inputs = options["include_unsafe"].get_bool();
     556           0 :         }
     557             : 
     558           0 :         if (options.exists("feeRate")) {
     559           0 :             if (options.exists("fee_rate")) {
     560           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both fee_rate (" + CURRENCY_ATOM + "/B) and feeRate (" + CURRENCY_UNIT + "/kB)");
     561             :             }
     562           0 :             if (options.exists("conf_target")) {
     563           0 :                 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           0 :             if (options.exists("estimate_mode")) {
     566           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
     567             :             }
     568           0 :             coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
     569           0 :             coinControl.fOverrideFeeRate = true;
     570           0 :         }
     571             : 
     572           0 :         if (options.exists("subtractFeeFromOutputs") || options.exists("subtract_fee_from_outputs") )
     573           0 :             subtractFeeFromOutputs = (options.exists("subtract_fee_from_outputs") ? options["subtract_fee_from_outputs"] : options["subtractFeeFromOutputs"]).get_array();
     574             : 
     575           0 :         SetFeeEstimateMode(wallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
     576           0 :       }
     577           0 :     } else {
     578             :         // if options is null and not a bool
     579           0 :         coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
     580             :     }
     581             : 
     582           0 :     if (options.exists("solving_data")) {
     583           0 :         const UniValue solving_data = options["solving_data"].get_obj();
     584           0 :         if (solving_data.exists("pubkeys")) {
     585           0 :             for (const UniValue& pk_univ : solving_data["pubkeys"].get_array().getValues()) {
     586           0 :                 const std::string& pk_str = pk_univ.get_str();
     587           0 :                 if (!IsHex(pk_str)) {
     588           0 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", pk_str));
     589             :                 }
     590           0 :                 const std::vector<unsigned char> data(ParseHex(pk_str));
     591           0 :                 const CPubKey pubkey(data.begin(), data.end());
     592           0 :                 if (!pubkey.IsFullyValid()) {
     593           0 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not a valid public key", pk_str));
     594             :                 }
     595           0 :                 coinControl.m_external_provider.pubkeys.emplace(pubkey.GetID(), pubkey);
     596             :                 // Add script for pubkeys
     597           0 :                 const CScript pk_script = GetScriptForDestination(PKHash(pubkey));
     598           0 :                 coinControl.m_external_provider.scripts.emplace(CScriptID(pk_script), pk_script);
     599           0 :             }
     600           0 :         }
     601             : 
     602           0 :         if (solving_data.exists("scripts")) {
     603           0 :             for (const UniValue& script_univ : solving_data["scripts"].get_array().getValues()) {
     604           0 :                 const std::string& script_str = script_univ.get_str();
     605           0 :                 if (!IsHex(script_str)) {
     606           0 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", script_str));
     607             :                 }
     608           0 :                 std::vector<unsigned char> script_data(ParseHex(script_str));
     609           0 :                 const CScript script(script_data.begin(), script_data.end());
     610           0 :                 coinControl.m_external_provider.scripts.emplace(CScriptID(script), script);
     611           0 :             }
     612           0 :         }
     613             : 
     614           0 :         if (solving_data.exists("descriptors")) {
     615           0 :             for (const UniValue& desc_univ : solving_data["descriptors"].get_array().getValues()) {
     616           0 :                 const std::string& desc_str  = desc_univ.get_str();
     617           0 :                 FlatSigningProvider desc_out;
     618           0 :                 std::string error;
     619           0 :                 std::vector<CScript> scripts_temp;
     620           0 :                 std::unique_ptr<Descriptor> desc = Parse(desc_str, desc_out, error, true);
     621           0 :                 if (!desc) {
     622           0 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unable to parse descriptor '%s': %s", desc_str, error));
     623             :                 }
     624           0 :                 desc->Expand(0, desc_out, scripts_temp, desc_out);
     625           0 :                 coinControl.m_external_provider.Merge(std::move(desc_out));
     626           0 :             }
     627           0 :         }
     628           0 :     }
     629             : 
     630           0 :     if (options.exists("input_sizes")) {
     631           0 :         for (const UniValue& input : options["input_sizes"].get_array().getValues()) {
     632           0 :             uint256 txid = ParseHashO(input, "txid");
     633             : 
     634           0 :             const UniValue& vout_v = input.find_value("vout");
     635           0 :             if (!vout_v.isNum()) {
     636           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
     637             :             }
     638           0 :             int vout = vout_v.getInt<int>();
     639           0 :             if (vout < 0) {
     640           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
     641             :             }
     642             : 
     643           0 :             const UniValue& weight_v = input.find_value("size");
     644           0 :             if (!weight_v.isNum()) {
     645           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing size key");
     646             :             }
     647           0 :             int64_t size = weight_v.getInt<int64_t>();
     648           0 :             const int64_t min_input_size = ::GetSerializeSize(CTxIn());
     649           0 :             CHECK_NONFATAL(min_input_size == 41);
     650           0 :             if (size < min_input_size) {
     651           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, size cannot be less than 41 bytes (size of outpoint + sequence + empty scriptSig)");
     652             :             }
     653           0 :             if (size > MAX_STANDARD_TX_SIZE) {
     654           0 :                 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           0 :             coinControl.SetInputWeight(COutPoint(txid, vout), size);
     658             :         }
     659           0 :     }
     660             : 
     661           0 :     if (tx.vout.size() == 0)
     662           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
     663             : 
     664           0 :     if (change_position != -1 && (change_position < 0 || (unsigned int)change_position > tx.vout.size()))
     665           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
     666             : 
     667           0 :     for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
     668           0 :         int pos = subtractFeeFromOutputs[idx].getInt<int>();
     669           0 :         if (setSubtractFeeFromOutputs.count(pos))
     670           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos));
     671           0 :         if (pos < 0)
     672           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos));
     673           0 :         if (pos >= int(tx.vout.size()))
     674           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos));
     675           0 :         setSubtractFeeFromOutputs.insert(pos);
     676           0 :     }
     677             : 
     678           0 :     bilingual_str error;
     679             : 
     680           0 :     if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
     681           0 :         throw JSONRPCError(RPC_WALLET_ERROR, error.original);
     682             :     }
     683           0 : }
     684             : 
     685           0 : static void SetOptionsInputWeights(const UniValue& inputs, UniValue& options)
     686             : {
     687           0 :     if (options.exists("input_sizes")) {
     688           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Input sizes should be specified in inputs rather than in options.");
     689             :     }
     690           0 :     if (inputs.size() == 0) {
     691           0 :         return;
     692             :     }
     693           0 :     UniValue sizes(UniValue::VARR);
     694           0 :     for (const UniValue& input : inputs.getValues()) {
     695           0 :         if (input.exists("size")) {
     696           0 :             sizes.push_back(input);
     697           0 :         }
     698             :     }
     699           0 :     options.pushKV("input_sizes", sizes);
     700           0 : }
     701             : 
     702           8 : RPCHelpMan fundrawtransaction()
     703             : {
     704          16 :     return RPCHelpMan{"fundrawtransaction",
     705           8 :                 "\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          24 :                 {
     718           8 :                     {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
     719          16 :                     {"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           8 :                         Cat<std::vector<RPCArg>>(
     721          88 :                         {
     722           8 :                             {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{true}, "For a transaction with existing inputs, automatically include more if they are not enough."},
     723           8 :                             {"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           8 :                             {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The Dash address to receive the change"},
     727           8 :                             {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
     728           8 :                             {"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           8 :                             {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
     732           8 :                             {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     733           8 :                             {"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kB."},
     734          16 :                             {"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          16 :                                 {
     739           8 :                                     {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
     740             :                                 },
     741             :                             },
     742          16 :                             {"input_sizes", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding sizes",
     743          16 :                                 {
     744          16 :                                     {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
     745          32 :                                         {
     746           8 :                                             {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
     747           8 :                                             {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
     748           8 :                                             {"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           8 :                         FundTxDoc()),
     758           8 :                         "options"},
     759             :                 },
     760           8 :                 RPCResult{
     761           8 :                     RPCResult::Type::OBJ, "", "",
     762          32 :                     {
     763           8 :                         {RPCResult::Type::STR_HEX, "hex", "The resulting raw transaction (hex-encoded string)"},
     764           8 :                         {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
     765           8 :                         {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
     766             :                     }
     767             :                                 },
     768           8 :                                 RPCExamples{
     769             :                             "\nCreate a transaction with no inputs\n"
     770           8 :                             + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
     771             :                             "\nAdd sufficient unsigned inputs to meet the output value\n"
     772           8 :                             + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
     773             :                             "\nSign the transaction\n"
     774           8 :                             + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
     775             :                             "\nSend the transaction\n"
     776           8 :                             + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
     777             :                                 },
     778           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     779             : {
     780           0 :     RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
     781             : 
     782           0 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     783           0 :     if (!pwallet) return UniValue::VNULL;
     784             : 
     785             :     // parse hex string from parameter
     786           0 :     CMutableTransaction tx;
     787           0 :     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           0 :     CCoinControl coin_control;
     794             :     // Automatically select (additional) coins. Can be overridden by options.add_inputs.
     795           0 :     coin_control.m_allow_other_inputs = true;
     796           0 :     FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control, /*override_min_fee=*/true);
     797             : 
     798           0 :     UniValue result(UniValue::VOBJ);
     799           0 :     result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
     800           0 :     result.pushKV("fee", ValueFromAmount(fee));
     801           0 :     result.pushKV("changepos", change_position);
     802             : 
     803           0 :     return result;
     804           0 : },
     805             :     };
     806           0 : }
     807             : 
     808           8 : RPCHelpMan signrawtransactionwithwallet()
     809             : {
     810          16 :     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           8 :                 "this transaction depends on but may not yet be in the block chain." +
     814             :         HELP_REQUIRING_PASSPHRASE,
     815          32 :                 {
     816           8 :                     {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
     817          16 :                     {"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The previous dependent transaction outputs",
     818          16 :                         {
     819          16 :                             {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
     820          48 :                                 {
     821           8 :                                     {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
     822           8 :                                     {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
     823           8 :                                     {"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
     824           8 :                                     {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH)"},
     825           8 :                                     {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
     826             :                                 },
     827             :                             },
     828             :                         },
     829             :                     },
     830           8 :                     {"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           8 :                 RPCResult{
     839           8 :                     RPCResult::Type::OBJ, "", "",
     840          32 :                     {
     841           8 :                         {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
     842           8 :                         {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
     843          16 :                         {RPCResult::Type::ARR, "errors", /*optional=*/true, "Script verification errors (if there are any)",
     844          16 :                         {
     845          16 :                             {RPCResult::Type::OBJ, "", "",
     846          48 :                             {
     847           8 :                                 {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
     848           8 :                                 {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
     849           8 :                                 {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
     850           8 :                                 {RPCResult::Type::NUM, "sequence", "Script sequence number"},
     851           8 :                                 {RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
     852             :                             }},
     853             :                         }},
     854             :                     }
     855             :                 },
     856           8 :                 RPCExamples{
     857           8 :                     HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
     858           8 :             + HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")
     859             :                 },
     860           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     861             : {
     862           0 :     RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
     863             : 
     864           0 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
     865           0 :     if (!pwallet) return UniValue::VNULL;
     866             : 
     867           0 :     CMutableTransaction mtx;
     868           0 :     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           0 :     LOCK(pwallet->cs_wallet);
     874           0 :     EnsureWalletIsUnlocked(*pwallet);
     875             : 
     876             :     // Fetch previous transactions (inputs):
     877           0 :     std::map<COutPoint, Coin> coins;
     878           0 :     for (const CTxIn& txin : mtx.vin) {
     879           0 :         coins[txin.prevout]; // Create empty map entry keyed by prevout.
     880             :     }
     881           0 :     pwallet->chain().findCoins(coins);
     882             : 
     883             :     // Parse the prevtxs array
     884           0 :     ParsePrevouts(request.params[1], nullptr, coins);
     885             : 
     886           0 :     int nHashType = ParseSighashString(request.params[2]);
     887             : 
     888             :     // Script verification errors
     889           0 :     std::map<int, bilingual_str> input_errors;
     890             : 
     891           0 :     bool complete = pwallet->SignTransaction(mtx, coins, nHashType, input_errors);
     892           0 :     UniValue result(UniValue::VOBJ);
     893           0 :     SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
     894           0 :     return result;
     895           0 : },
     896             :     };
     897           0 : }
     898             : 
     899           8 : RPCHelpMan send()
     900             : {
     901          16 :     return RPCHelpMan{"send",
     902           8 :         "\nEXPERIMENTAL warning: this call may be changed in future releases.\n"
     903             :         "\nSend a transaction.\n",
     904          48 :         {
     905          16 :             {"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          24 :                 {
     910          16 :                     {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
     911          16 :                         {
     912           8 :                             {"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          16 :                     {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
     916          16 :                         {
     917           8 :                             {"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           8 :             {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
     923          16 :             {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
     924           8 :                         "       \"" + FeeModes("\"\n\"") + "\""},
     925           8 :             {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     926          16 :             {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
     927           8 :                 Cat<std::vector<RPCArg>>(
     928         104 :                 {
     929           8 :                     {"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           8 :                     {"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           8 :                     {"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           8 :                     {"change_address", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The Dash address to receive the change"},
     935           8 :                     {"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
     936           8 :                     {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
     937           8 :                     {"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          16 :                     {"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Specify inputs instead of adding them automatically. A JSON array of JSON objects",
     941          40 :                         {
     942           8 :                             {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
     943           8 :                             {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
     944           8 :                             {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
     945           8 :                             {"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           8 :                     {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
     952           8 :                     {"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
     953           8 :                     {"psbt", RPCArg::Type::BOOL,  RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
     954          16 :                     {"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          16 :                         {
     959           8 :                             {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
     960             :                         },
     961             :                     },
     962             :                 },
     963           8 :                 FundTxDoc()),
     964           8 :                 "options"},
     965             :         },
     966           8 :         RPCResult{
     967           8 :             RPCResult::Type::OBJ, "", "",
     968          40 :                 {
     969           8 :                     {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
     970           8 :                     {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           8 :                     {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
     972           8 :                     {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           8 :         RPCExamples{""
     976             :         "\nSend 0.1 Dash with a confirmation target of 6 blocks in economical fee estimate mode\n"
     977           8 :         + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 6 economical\n") +
     978           8 :         "Send 0.2 Dash with a fee rate of 1.1 " + CURRENCY_ATOM + "/B using positional arguments\n"
     979           8 :         + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" 1.1\n") +
     980           8 :         "Send 0.2 Dash with a fee rate of 1 " + CURRENCY_ATOM + "/B using the options argument\n"
     981           8 :         + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" null '{\"fee_rate\": 1}'\n") +
     982           8 :         "Send 0.3 Dash with a fee rate of 25 " + CURRENCY_ATOM + "/B using named arguments\n"
     983           8 :         + 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           8 :         + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 economical '{\"add_to_wallet\": false, \"inputs\": [{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\", \"vout\":1}]}'")
     986             :         },
     987           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     988             :         {
     989           0 :             RPCTypeCheck(request.params, {
     990           0 :                 UniValueType(), // outputs (ARR or OBJ, checked later)
     991           0 :                 UniValue::VNUM, // conf_target
     992           0 :                 UniValue::VSTR, // estimate_mode
     993           0 :                 UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
     994           0 :                 UniValue::VOBJ, // options
     995             :                 }, true
     996             :             );
     997             : 
     998           0 :             std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     999           0 :             if (!pwallet) return UniValue::VNULL;
    1000             : 
    1001           0 :             UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
    1002           0 :             InterpretFeeEstimationInstructions(/*conf_target=*/request.params[1], /*estimate_mode=*/request.params[2], /*fee_rate=*/request.params[3], options);
    1003           0 :             PreventOutdatedOptions(options);
    1004             : 
    1005             : 
    1006             :             CAmount fee;
    1007             :             int change_position;
    1008           0 :             CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"]);
    1009           0 :             CCoinControl coin_control;
    1010             :             // Automatically select coins, unless at least one is manually selected. Can
    1011             :             // be overridden by options.add_inputs.
    1012           0 :             coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
    1013           0 :             SetOptionsInputWeights(options["inputs"], options);
    1014           0 :             FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
    1015             : 
    1016           0 :             return FinishTransaction(pwallet, options, rawTx);
    1017           0 :         }
    1018             :     };
    1019           0 : }
    1020             : 
    1021           8 : RPCHelpMan sendall()
    1022             : {
    1023          16 :     return RPCHelpMan{"sendall",
    1024           8 :         "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          48 :         {
    1029          16 :             {"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          24 :                 {
    1032           8 :                     {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "A Dash address which receives an equal share of the unspecified amount."},
    1033          16 :                     {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
    1034          16 :                         {
    1035           8 :                             {"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           8 :             {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
    1041          16 :             {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
    1042           8 :                         "       \"" + FeeModes("\"\n\"") + "\""},
    1043           8 :             {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
    1044           8 :             {
    1045           8 :                 "options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
    1046           8 :                 Cat<std::vector<RPCArg>>(
    1047          72 :                     {
    1048           8 :                         {"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns the serialized transaction without broadcasting or adding it to the wallet"},
    1049           8 :                         {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
    1050           8 :                         {"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          16 :                         {"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          16 :                             {
    1055          16 :                                 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
    1056          32 :                                     {
    1057           8 :                                         {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
    1058           8 :                                         {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
    1059           8 :                                         {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' argument"}, "The sequence number"},
    1060             :                                     },
    1061             :                                 },
    1062             :                             },
    1063             :                         },
    1064           8 :                         {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
    1065           8 :                         {"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
    1066           8 :                         {"psbt", RPCArg::Type::BOOL,  RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
    1067           8 :                         {"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           8 :                     FundTxDoc()
    1070             :                 ),
    1071           8 :                 "options"
    1072             :             },
    1073             :         },
    1074           8 :         RPCResult{
    1075           8 :             RPCResult::Type::OBJ, "", "",
    1076          40 :                 {
    1077           8 :                     {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
    1078           8 :                     {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           8 :                     {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
    1080           8 :                     {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           8 :         RPCExamples{""
    1084           8 :         "\nSpend all UTXOs from the wallet with a fee rate of 1 " + CURRENCY_ATOM + "/B using named arguments\n"
    1085           8 :         + HelpExampleCli("-named sendall", "recipients='[\"" + EXAMPLE_ADDRESS[0] + "\"]' fee_rate=1\n") +
    1086           8 :         "Spend all UTXOs with a fee rate of 1.1 " + CURRENCY_ATOM + "/B using positional arguments\n"
    1087           8 :         + HelpExampleCli("sendall", "'[\"" + EXAMPLE_ADDRESS[0] + "\"]' null \"unset\" 1.1\n") +
    1088           8 :         "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           8 :         + HelpExampleCli("sendall", "'[\"" + EXAMPLE_ADDRESS[0] + "\", \"" + EXAMPLE_ADDRESS[1] + "\"]' null \"unset\" null '{\"fee_rate\": 1.5}'\n") +
    1090           8 :         "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           8 :         + HelpExampleCli("sendall", "'[\"" + EXAMPLE_ADDRESS[0] + "\"]' null \"unset\" null '{\"fee_rate\": 10, \"send_max\": true}'\n") +
    1092           8 :         "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           8 :         + HelpExampleCli("-named sendall", "recipients='[{\"" + EXAMPLE_ADDRESS[1] + "\": 0.25}, \""+ EXAMPLE_ADDRESS[0] + "\"]' fee_rate=1.3\n")
    1094             :         },
    1095           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1096             :         {
    1097           0 :             RPCTypeCheck(request.params, {
    1098           0 :                 UniValue::VARR, // recipients
    1099           0 :                 UniValue::VNUM, // conf_target
    1100           0 :                 UniValue::VSTR, // estimate_mode
    1101           0 :                 UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
    1102           0 :                 UniValue::VOBJ, // options
    1103             :                 }, true
    1104             :             );
    1105             : 
    1106           0 :             std::shared_ptr<CWallet> const pwallet{GetWalletForJSONRPCRequest(request)};
    1107           0 :             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           0 :             pwallet->BlockUntilSyncedToCurrentChain();
    1111             : 
    1112           0 :             UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
    1113           0 :             InterpretFeeEstimationInstructions(/*conf_target=*/request.params[1], /*estimate_mode=*/request.params[2], /*fee_rate=*/request.params[3], options);
    1114           0 :             PreventOutdatedOptions(options);
    1115             : 
    1116             : 
    1117           0 :             std::set<std::string> addresses_without_amount;
    1118           0 :             UniValue recipient_key_value_pairs(UniValue::VARR);
    1119           0 :             const UniValue& recipients{request.params[0]};
    1120           0 :             for (unsigned int i = 0; i < recipients.size(); ++i) {
    1121           0 :                 const UniValue& recipient{recipients[i]};
    1122           0 :                 if (recipient.isStr()) {
    1123           0 :                     UniValue rkvp(UniValue::VOBJ);
    1124           0 :                     rkvp.pushKV(recipient.get_str(), 0);
    1125           0 :                     recipient_key_value_pairs.push_back(rkvp);
    1126           0 :                     addresses_without_amount.insert(recipient.get_str());
    1127           0 :                 } else {
    1128           0 :                     recipient_key_value_pairs.push_back(recipient);
    1129             :                 }
    1130           0 :             }
    1131             : 
    1132           0 :             if (addresses_without_amount.size() == 0) {
    1133           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Must provide at least one address without a specified amount");
    1134             :             }
    1135             : 
    1136           0 :             CCoinControl coin_control;
    1137             : 
    1138           0 :             SetFeeEstimateMode(*pwallet, coin_control, options["conf_target"], options["estimate_mode"], options["fee_rate"], /*override_min_fee=*/false);
    1139             : 
    1140           0 :             coin_control.fAllowWatchOnly = ParseIncludeWatchonly(options["include_watching"], *pwallet);
    1141             : 
    1142           0 :             FeeCalculation fee_calc_out;
    1143           0 :             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           0 :             if (coin_control.m_feerate && fee_rate > *coin_control.m_feerate) {
    1147           0 :                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           0 :             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           0 :             CMutableTransaction rawTx{ConstructTransaction(options["inputs"], recipient_key_value_pairs, options["locktime"])};
    1155           0 :             LOCK(pwallet->cs_wallet);
    1156             : 
    1157           0 :             CAmount total_input_value(0);
    1158           0 :             bool send_max{options.exists("send_max") ? options["send_max"].get_bool() : false};
    1159           0 :             if (options.exists("inputs") && options.exists("send_max")) {
    1160           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot combine send_max with specific inputs.");
    1161           0 :             } else if (options.exists("inputs")) {
    1162           0 :                 for (const CTxIn& input : rawTx.vin) {
    1163           0 :                     if (pwallet->IsSpent(input.prevout)) {
    1164           0 :                         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) was already spent.", input.prevout.hash.ToString(), input.prevout.n));
    1165             :                     }
    1166           0 :                     const CWalletTx* tx{pwallet->GetWalletTx(input.prevout.hash)};
    1167           0 :                     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           0 :                         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           0 :                     total_input_value += tx->tx->vout[input.prevout.n].nValue;
    1171             :                 }
    1172           0 :             } else {
    1173           0 :                 for (const COutput& output : AvailableCoins(*pwallet, &coin_control, fee_rate, /*nMinimumAmount=*/0).all()) {
    1174           0 :                     CHECK_NONFATAL(output.input_bytes > 0);
    1175           0 :                     if (send_max && fee_rate.GetFee(output.input_bytes) > output.txout.nValue) {
    1176           0 :                         continue;
    1177             :                     }
    1178           0 :                     CTxIn input(output.outpoint.hash, output.outpoint.n, CScript(), CTxIn::SEQUENCE_FINAL);
    1179           0 :                     rawTx.vin.push_back(input);
    1180           0 :                     total_input_value += output.txout.nValue;
    1181           0 :                 }
    1182             :             }
    1183             : 
    1184             :             // estimate final size of tx
    1185           0 :             const int64_t tx_size{CalculateMaximumSignedTxSize(CTransaction(rawTx), pwallet.get())};
    1186           0 :             const CAmount fee_from_size{fee_rate.GetFee(tx_size)};
    1187           0 :             const CAmount effective_value{total_input_value - fee_from_size};
    1188             : 
    1189           0 :             if (fee_from_size > pwallet->m_default_max_tx_fee) {
    1190           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED).original);
    1191             :             }
    1192             : 
    1193           0 :             if (effective_value <= 0) {
    1194           0 :                 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           0 :                     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           0 :             if (tx_size > MAX_STANDARD_TX_SIZE) {
    1203           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction too large.");
    1204             :             }
    1205             : 
    1206           0 :             CAmount output_amounts_claimed{0};
    1207           0 :             for (CTxOut out : rawTx.vout) {
    1208           0 :                 output_amounts_claimed += out.nValue;
    1209           0 :             }
    1210             : 
    1211           0 :             if (output_amounts_claimed > total_input_value) {
    1212           0 :                 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Assigned more value to outputs than available funds.");
    1213             :             }
    1214             : 
    1215           0 :             const CAmount remainder{effective_value - output_amounts_claimed};
    1216           0 :             if (remainder < 0) {
    1217           0 :                 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds for fees after creating specified outputs.");
    1218             :             }
    1219             : 
    1220           0 :             const CAmount per_output_without_amount{remainder / (long)addresses_without_amount.size()};
    1221             : 
    1222           0 :             bool gave_remaining_to_first{false};
    1223           0 :             for (CTxOut& out : rawTx.vout) {
    1224           0 :                 CTxDestination dest;
    1225           0 :                 ExtractDestination(out.scriptPubKey, dest);
    1226           0 :                 std::string addr{EncodeDestination(dest)};
    1227           0 :                 if (addresses_without_amount.count(addr) > 0) {
    1228           0 :                     out.nValue = per_output_without_amount;
    1229           0 :                     if (!gave_remaining_to_first) {
    1230           0 :                         out.nValue += remainder % addresses_without_amount.size();
    1231           0 :                         gave_remaining_to_first = true;
    1232           0 :                     }
    1233           0 :                     if (IsDust(out, pwallet->chain().relayDustFee())) {
    1234             :                         // Dynamically generated output amount is dust
    1235           0 :                         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Dynamically assigned remainder results in dust output.");
    1236             :                     }
    1237           0 :                 } else {
    1238           0 :                     if (IsDust(out, pwallet->chain().relayDustFee())) {
    1239             :                         // Specified output amount is dust
    1240           0 :                         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Specified output amount to %s is below dust threshold.", addr));
    1241             :                     }
    1242             :                 }
    1243           0 :             }
    1244             : 
    1245           0 :             const bool lock_unspents{options.exists("lock_unspents") ? options["lock_unspents"].get_bool() : false};
    1246           0 :             if (lock_unspents) {
    1247           0 :                 for (const CTxIn& txin : rawTx.vin) {
    1248           0 :                     pwallet->LockCoin(txin.prevout);
    1249             :                 }
    1250           0 :             }
    1251             : 
    1252           0 :             return FinishTransaction(pwallet, options, rawTx);
    1253           0 :         }
    1254             :     };
    1255           0 : }
    1256             : 
    1257           8 : RPCHelpMan walletprocesspsbt()
    1258             : {
    1259          16 :     return RPCHelpMan{"walletprocesspsbt",
    1260             :                 "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
    1261           8 :                 "that we can sign for." +
    1262             :         HELP_REQUIRING_PASSPHRASE,
    1263          48 :                 {
    1264           8 :                     {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
    1265           8 :                     {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating (requires wallet to be unlocked)"},
    1266           8 :                     {"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           8 :                     {"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
    1274           8 :                     {"finalize", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also finalize inputs if possible"},
    1275             :                 },
    1276           8 :                 RPCResult{
    1277           8 :                     RPCResult::Type::OBJ, "", "",
    1278          24 :                     {
    1279           8 :                         {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"},
    1280           8 :                         {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
    1281             :                     }
    1282             :                 },
    1283           8 :                 RPCExamples{
    1284           8 :                     HelpExampleCli("walletprocesspsbt", "\"psbt\"")
    1285             :                 },
    1286           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1287             : {
    1288           0 :     RPCTypeCheck(request.params, {UniValue::VSTR});
    1289             : 
    1290           0 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
    1291           0 :     if (!pwallet) return UniValue::VNULL;
    1292             : 
    1293           0 :     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           0 :     wallet.BlockUntilSyncedToCurrentChain();
    1297             : 
    1298             :     // Unserialize the transaction
    1299           0 :     PartiallySignedTransaction psbtx;
    1300           0 :     std::string error;
    1301           0 :     if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
    1302           0 :         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
    1303             :     }
    1304             : 
    1305             :     // Get the sighash type
    1306           0 :     int nHashType = ParseSighashString(request.params[2]);
    1307             : 
    1308             :     // Use CTransaction for the constant parts of the
    1309             :     // transaction to avoid rehashing.
    1310           0 :     const CTransaction txConst(*psbtx.tx);
    1311             : 
    1312             :     // Fill transaction with our data and also sign
    1313           0 :     bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
    1314           0 :     bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
    1315           0 :     bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
    1316           0 :     bool complete = true;
    1317             : 
    1318           0 :     if (sign) EnsureWalletIsUnlocked(*pwallet);
    1319             : 
    1320           0 :     const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs, nullptr, finalize)};
    1321           0 :     if (err != TransactionError::OK) {
    1322           0 :         throw JSONRPCTransactionError(err);
    1323             :     }
    1324             : 
    1325           0 :     UniValue result(UniValue::VOBJ);
    1326           0 :     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    1327           0 :     ssTx << psbtx;
    1328           0 :     result.pushKV("psbt", EncodeBase64(ssTx.str()));
    1329           0 :     result.pushKV("complete", complete);
    1330             : 
    1331           0 :     return result;
    1332           0 : },
    1333             :     };
    1334           0 : }
    1335             : 
    1336           8 : RPCHelpMan walletcreatefundedpsbt()
    1337             : {
    1338          16 :     return RPCHelpMan{"walletcreatefundedpsbt",
    1339           8 :                 "\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          48 :                 {
    1344          16 :                     {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Leave empty to add inputs automatically. See add_inputs option.",
    1345          16 :                         {
    1346          16 :                             {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
    1347          40 :                                 {
    1348           8 :                                     {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
    1349           8 :                                     {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
    1350           8 :                                     {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' argument"}, "The sequence number"},
    1351           8 :                                     {"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          16 :                     {"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          24 :                         {
    1364          16 :                             {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
    1365          16 :                                 {
    1366           8 :                                     {"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          16 :                             {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
    1370          16 :                                 {
    1371           8 :                                     {"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           8 :                     {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
    1377          16 :                     {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
    1378           8 :                         Cat<std::vector<RPCArg>>(
    1379          80 :                         {
    1380           8 :                             {"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           8 :                             {"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           8 :                             {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The Dash address to receive the change"},
    1385           8 :                             {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
    1386           8 :                             {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"},
    1387           8 :                             {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
    1388           8 :                             {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/B."},
    1389           8 :                             {"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set: makes wallet determine the fee"}, "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
    1390          16 :                             {"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          16 :                                 {
    1395           8 :                                     {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
    1396             :                                 },
    1397             :                             },
    1398             :                         },
    1399           8 :                         FundTxDoc()),
    1400           8 :                         "options"},
    1401           8 :                     {"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
    1402             :                 },
    1403           8 :                 RPCResult{
    1404           8 :                     RPCResult::Type::OBJ, "", "",
    1405          32 :                     {
    1406           8 :                         {RPCResult::Type::STR, "psbt", "The resulting raw transaction (base64-encoded string)"},
    1407           8 :                         {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
    1408           8 :                         {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
    1409             :                     }
    1410             :                                 },
    1411           8 :                                 RPCExamples{
    1412             :                             "\nCreate a transaction with no inputs\n"
    1413           8 :                             + HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
    1414             :                                 },
    1415           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1416             : {
    1417           0 :     RPCTypeCheck(request.params, {
    1418           0 :         UniValue::VARR,
    1419           0 :         UniValueType(), // ARR or OBJ, checked later
    1420           0 :         UniValue::VNUM,
    1421           0 :         UniValue::VOBJ,
    1422           0 :         UniValue::VBOOL,
    1423             :         }, true
    1424             :     );
    1425             : 
    1426           0 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
    1427           0 :     if (!pwallet) return UniValue::VNULL;
    1428             : 
    1429           0 :     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           0 :     wallet.BlockUntilSyncedToCurrentChain();
    1433             : 
    1434           0 :     UniValue options{request.params[3].isNull() ? UniValue::VOBJ : request.params[3]};
    1435             : 
    1436             :     CAmount fee;
    1437             :     int change_position;
    1438           0 :     CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2]);
    1439           0 :     CCoinControl coin_control;
    1440             :     // Automatically select coins, unless at least one is manually selected. Can
    1441             :     // be overridden by options.add_inputs.
    1442           0 :     coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
    1443           0 :     SetOptionsInputWeights(request.params[0], options);
    1444           0 :     FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /*override_min_fee=*/true);
    1445             : 
    1446             :     // Make a blank psbt
    1447           0 :     PartiallySignedTransaction psbtx{rawTx};
    1448             : 
    1449             :     // Fill transaction with out data but don't sign
    1450           0 :     bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
    1451           0 :     bool complete = true;
    1452           0 :     const TransactionError err{wallet.FillPSBT(psbtx, complete, 1, false, bip32derivs)};
    1453           0 :     if (err != TransactionError::OK) {
    1454           0 :         throw JSONRPCTransactionError(err);
    1455             :     }
    1456             : 
    1457             :     // Serialize the PSBT
    1458           0 :     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    1459           0 :     ssTx << psbtx;
    1460             : 
    1461           0 :     UniValue result(UniValue::VOBJ);
    1462           0 :     result.pushKV("psbt", EncodeBase64(ssTx.str()));
    1463           0 :     result.pushKV("fee", ValueFromAmount(fee));
    1464           0 :     result.pushKV("changepos", change_position);
    1465           0 :     return result;
    1466           0 : },
    1467             :     };
    1468           0 : }
    1469             : } // namespace wallet

Generated by: LCOV version 1.16