fix(fullnode): work around daemon note-selection fee-gap on shielded sends
dragonxd's z_sendmany picks notes to cover the recipient total (nTotalOut) but not the miner fee, then rejects the build unless the selected notes cover amount+fee (rpcwallet.cpp:5312 vs asyncrpcoperation_sendmany.cpp:278). So a shielded send whose largest notes sum exactly to the amount fails with "Insufficient shielded funds, have H, need H+fee" despite ample balance — e.g. sending exactly 2.0 from an address whose biggest note is 2.0. Since the failure is async (reported via the opid poll), detect it there: when a shielded send fails with that message and the selected total H >= the requested amount (selection covered the amount but stopped one note short of the fee — vs a genuine shortfall where H < amount), re-issue the send once with a tiny self-output (= fee) back to the from-address. That lifts the daemon's selection target past the boundary so it grabs another note and can cover the fee; the recipient still receives the exact amount. Retries are tracked so a second failure surfaces normally (no loop). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -865,6 +865,11 @@ void App::update()
|
||||
// its own error toast); otherwise surface a generic notification (this is
|
||||
// how shield/merge/auto-shield failures become visible).
|
||||
for (const auto& [opid, rawMsg] : parsed.failureByOpid) {
|
||||
// Auto-work-around the daemon's note-selection fee-gap: re-issues the send with a
|
||||
// self-output so it covers the fee. If a retry was issued, defer the outcome to it.
|
||||
if (maybeRetrySendForFeeGap(opid, rawMsg)) {
|
||||
continue;
|
||||
}
|
||||
std::string msg = sendErrorNeedsRescan(rawMsg)
|
||||
? rawMsg + "\n\n" + TR("send_err_needs_rescan")
|
||||
: rawMsg;
|
||||
|
||||
Reference in New Issue
Block a user