Repository: bitcoin/bitcoin
File: src/common/messages.cpp (C++)

Code before:
}

std::string InvalidEstimateModeErrorMessage()
{
    return "Invalid estimate_mode parameter, must be one of: \"" + FeeModes("\", \"") + "\"";
}

bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode)
{
    auto searchkey = ToUpper(mode_string);
    for (const auto& pair : FeeModeMap()) {
        if (ToUpper(pair.first) == searchkey) {
            fee_estimate_mode = pair.second;
            return true;
        }
    }
    return false;
}

bilingual_str PSBTErrorString(PSBTError err)
{
    switch (err) {
        case PSBTError::MISSING_INPUTS:
            return Untranslated("Inputs missing or spent");
        case PSBTError::SIGHASH_MISMATCH:
            return Untranslated("Specified sighash value does not match value stored in PSBT");
        case PSBTError::EXTERNAL_SIGNER_NOT_FOUND:
            return Untranslated("External signer not found");
        case PSBTError::EXTERNAL_SIGNER_FAILED:
            return Untranslated("External signer failed to sign");
        case PSBTError::UNSUPPORTED:
            return Untranslated("Signer does not support PSBT");
        // no default case, so the compiler can warn about missing cases
    }
    assert(false);
}

bilingual_str TransactionErrorString(const TransactionError err)
{
    switch (err) {
        case TransactionError::OK:
            return Untranslated("No error");
        case TransactionError::MISSING_INPUTS:
            return Untranslated("Inputs missing or spent");
        case TransactionError::ALREADY_IN_CHAIN:
            return Untranslated("Transaction already in block chain");
        case TransactionError::MEMPOOL_REJECTED:
            return Untranslated("Transaction rejected by mempool");
        case TransactionError::MEMPOOL_ERROR:
            return Untranslated("Mempool internal error");
        case TransactionError::MAX_FEE_EXCEEDED:

Review comment (suggestion):
nitty nit (feel free to ignore):
If changing this file, could add a comment for clarity, although it's a small enough function for a reader to pick up on the meaning of the magic number 2.

```c++
modes = modes.substr(0, modes.size() - 2) + "\n"; // remove the ", " delimiter
```

Alternatively, could add a string to name the delimiter.  Something like:
 ```diff
 diff --git a/src/common/messages.cpp b/src/common/messages.cpp
index bf41d09bbd..76777dd8cd 100644
--- a/src/common/messages.cpp
+++ b/src/common/messages.cpp
@@ -76,12 +76,13 @@ std::string FeeModesDetail()
 {
     std::string modes{"Available modes: "};
     std::string info;
+   std::string delimiter{", "};
     for (const auto& fee_mode : FeeModeMap()) {
-        modes += strprintf("\"%s\", ", fee_mode.first);
+        modes += "\"" + fee_mode.first + "\"" + delimiter;
         info += FeeModeInfo(fee_mode);
     }
     if (!FeeModeMap().empty()) {
-        modes = modes.substr(0, modes.size() - 2) + "\n";
+        modes = modes.substr(0, modes.size() - delimiter.length()) + "\n";
     }
     return modes + info;
 }
```

Code after:
        case FeeEstimateMode::UNSET:
            return strprintf("%s means no mode set (%s). \n", mode.first, default_info);
        case FeeEstimateMode::ECONOMICAL:
            return strprintf("%s estimates use a shorter time horizon, making them more\n"
                   "responsive to short-term drops in the prevailing fee market. This mode\n"
                   "potentially returns a lower fee rate estimate.\n", mode.first);
        case FeeEstimateMode::CONSERVATIVE:
            return strprintf("%s estimates use a longer time horizon, making them\n"
                   "less responsive to short-term drops in the prevailing fee market. This mode\n"
                   "potentially returns a higher fee rate estimate.\n", mode.first);
        default:
            // Other modes apart from the ones handled are fee rate units; they should not be clarified.
            assert(false);
    }
}

std::string FeeModesDetail(std::string default_info)
{
    std::string info;
    for (const auto& fee_mode : FeeModeMap()) {
        info += FeeModeInfo(fee_mode, default_info);
    }
    return strprintf("%s \n%s", FeeModes(", "), info);
}

std::string FeeModes(const std::string& delimiter)
{
    return Join(FeeModeMap(), delimiter, [&](const std::pair<std::string, FeeEstimateMode>& i) { return i.first; });
}

std::string InvalidEstimateModeErrorMessage()
{
    return "Invalid estimate_mode parameter, must be one of: \"" + FeeModes("\", \"") + "\"";
}

bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode)
{
    auto searchkey = ToUpper(mode_string);
    for (const auto& pair : FeeModeMap()) {
        if (ToUpper(pair.first) == searchkey) {
            fee_estimate_mode = pair.second;
            return true;
        }
    }
    return false;
}

bilingual_str PSBTErrorString(PSBTError err)
{
    switch (err) {
        case PSBTError::MISSING_INPUTS: