Repository: zcash/zcash (language: rust)

Issue:
Add a wrapper around std::visit to allow the value being matched to be given first
In code that looks like
```rust
std::visit(match {
    /* ... lots of code ... */
}, specimen)
```
I often have to scroll down to the end to see what `specimen` is and then scroll back, which is annoying.


Discussion / hints:

Fix patch:
diff --git a/src/consensus/params.cpp b/src/consensus/params.cpp
index 34e94f48ef5..51dfb183c27 100644
--- a/src/consensus/params.cpp
+++ b/src/consensus/params.cpp
@@ -188,7 +188,7 @@ namespace Consensus {
                 throw std::runtime_error("Funding stream address was not a valid " PACKAGE_NAME " address.");
             }
 
-            std::visit(match {
+            examine(addr.value(), match {
                 [&](const CKeyID& keyId) {
                     addresses.push_back(GetScriptForDestination(keyId));
                 },
@@ -201,7 +201,7 @@ namespace Consensus {
                 [&](const auto& zaddr) {
                     throw std::runtime_error("Funding stream address was not a valid transparent P2SH or Sapling address.");
                 }
-            }, addr.value());
+            });
         }
 
         auto validationResult = FundingStream::ValidateFundingStream(params, startHeight, endHeight, addresses);
diff --git a/src/key_io.cpp b/src/key_io.cpp
index 33fa6a710bf..524028d2db9 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -468,7 +468,7 @@ std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::s
     }
 
     // Finally, try parsing as transparent
-    return std::visit(match {
+    return examine(DecodeDestination(str), match {
         [](const CKeyID& keyIdIn) {
             std::optional<libzcash::PaymentAddress> keyId = keyIdIn;
             return keyId;
@@ -481,7 +481,7 @@ std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::s
             std::optional<libzcash::PaymentAddress> result = std::nullopt;
             return result;
         }
-    }, DecodeDestination(str));
+    });
 }
 
 bool KeyIO::IsValidPaymentAddressString(const std::string& str) const
diff --git a/src/keystore.cpp b/src/keystore.cpp
index be452294af1..0e35c915b21 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -19,10 +19,10 @@ std::optional<AddressUFVKMetadata> CKeyStore::GetUFVKMetadataForAddress(
         const CTxDestination& address) const
 {
     auto self = this;
-    return std::visit(match {
+    return examine(address, match {
             [](const CNoDestination&) -> std::optional<AddressUFVKMetadata> { return std::nullopt; },
             [&](const auto& addr) { return self->GetUFVKMetadataForReceiver(addr); }
-    }, address);
+    });
 }
 
 bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
@@ -428,7 +428,7 @@ CBasicKeyStore::GetUFVKIdForAddress(const libzcash::UnifiedAddress& addr) const
 std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const libzcash::ViewingKey& vk) const
 {
     std::optional<libzcash::UFVKId> result;
-    std::visit(match {
+    examine(vk, match {
         [&](const libzcash::SproutViewingKey& vk) {},
         [&](const libzcash::SaplingExtendedFullViewingKey& extfvk) {
             const auto saplingIvk = extfvk.ToIncomingViewingKey();
@@ -456,7 +456,7 @@ std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const lib
                 }
             }
         }
-    }, vk);
+    });
     return result;
 }
 
diff --git a/src/miner.cpp b/src/miner.cpp
index f8602cc38e8..6f2dd5cd068 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -351,11 +351,11 @@ void BlockAssembler::resetBlock(const MinerAddress& minerAddress)
 
     // Reserve space for coinbase tx
     // nBlockMaxSize already includes 1000 bytes for transaction structure overhead.
-    nBlockSize = std::visit(match {
+    nBlockSize = examine(minerAddress, match {
         [](const libzcash::OrchardRawAddress&) { return 9000; },
         [](const libzcash::SaplingPaymentAddress&) { return 1000; },
         [](const boost::shared_ptr<CReserveScript> &) { return 1000; },
-    }, minerAddress);
+    });
     nBlockSigOps = 100;
 
     // These counters do not include coinbase tx
@@ -812,14 +812,12 @@ std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::Sapl
 std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::UnifiedAddress &addr) const {
     auto preferred = addr.GetPreferredRecipientAddress(consensus, height);
     if (preferred.has_value()) {
-        std::optional<MinerAddress> ret;
-        std::visit(match {
-            [&](const libzcash::OrchardRawAddress addr) { ret = MinerAddress(addr); },
-            [&](const libzcash::SaplingPaymentAddress addr) { ret = MinerAddress(addr); },
-            [&](const CKeyID keyID) { ret = operator()(keyID); },
-            [&](const auto other) { ret = std::nullopt; }
-        }, preferred.value());
-        return ret;
+        return examine(preferred.value(), match {
+            [&](const libzcash::OrchardRawAddress addr) -> std::optional<MinerAddress> { return MinerAddress(addr); },
+            [&](const libzcash::SaplingPaymentAddress addr) -> std::optional<MinerAddress> { return MinerAddress(addr); },
+            [&](const CKeyID keyID) -> std::optional<MinerAddress> { return operator()(keyID); },
+            [&](const auto other) -> std::optional<MinerAddress> { return std::nullopt; }
+        });
     } else {
         return std::nullopt;
     }
diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp
index 0d8cba32790..2884dd82957 100644
--- a/src/transaction_builder.cpp
+++ b/src/transaction_builder.cpp
@@ -457,7 +457,7 @@ void TransactionBuilder::SendChangeTo(
     saplingChangeAddr = std::nullopt;
     sproutChangeAddr = std::nullopt;
 
-    std::visit(match {
+    examine(changeAddr, match {
         [&](const CKeyID& keyId) {
             tChangeAddr = keyId;
         },
@@ -470,7 +470,7 @@ void TransactionBuilder::SendChangeTo(
         [&](const libzcash::OrchardRawAddress& changeDest) {
             orchardChangeAddr = std::make_pair(ovk, changeDest);
         }
-    }, changeAddr);
+    });
 }
 
 void TransactionBuilder::SendChangeToSprout(const libzcash::SproutPaymentAddress& zaddr) {
diff --git a/src/util/match.h b/src/util/match.h
index 79aa972b891..e285149dc24 100644
--- a/src/util/match.h
+++ b/src/util/match.h
@@ -21,4 +21,17 @@ template<class... Ts> struct match : Ts... { using Ts::operator()...; };
 // explicit deduction guide (not needed as of C++20)
 template<class... Ts> match(Ts...) -> match<Ts...>;
 
+// A wrapper around two-argument `std::visit` that reverses the arguments, putting the
+// value to be visited first. This is normally used as:
+//
+// examine(specimen, match {
+//     ...
+// })
+//
+// The return type is inferred as it would be for `std::visit`.
+template<class Specimen, class Visitor>
+decltype(auto) examine(Specimen&& specimen, Visitor&& visitor) {
+    return std::visit(visitor, specimen);
+}
+
 #endif // ZCASH_UTIL_MATCH_H
diff --git a/src/wallet/asyncrpcoperation_common.cpp b/src/wallet/asyncrpcoperation_common.cpp
index f20fb625a8c..f03dad03a0c 100644
--- a/src/wallet/asyncrpcoperation_common.cpp
+++ b/src/wallet/asyncrpcoperation_common.cpp
@@ -47,7 +47,7 @@ void ThrowInputSelectionError(
         const ZTXOSelector& selector,
         const TransactionStrategy& strategy)
 {
-    std::visit(match {
+    examine(err, match {
         [](const AddressResolutionError& err) {
             switch (err) {
                 case AddressResolutionError::SproutRecipientsNotSupported:
@@ -105,7 +105,7 @@ void ThrowInputSelectionError(
                 strprintf(
                     "Insufficient funds: have %s, %s",
                     FormatMoney(err.available),
-                    std::visit(match {
+                    examine(err.reason, match {
                         [](const InsufficientFundsError& ife) {
                             return strprintf("need %s", FormatMoney(ife.required));
                         },
@@ -116,8 +116,7 @@ void ThrowInputSelectionError(
                                     FormatMoney(dte.changeAmount),
                                     FormatMoney(dte.dustThreshold));
                         }
-                    },
-                    err.reason))
+                    }))
                     + (selector.TransparentCoinbasePolicy() != TransparentCoinbasePolicy::Disallow
                        ? "" :
                        "; note that coinbase outputs will not be selected if you specify "
@@ -161,5 +160,5 @@ void ThrowInputSelectionError(
                     err.maxNotes,
                     err.orchardNotes));
         }
-    }, err);
+    });
 }
diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp
index 666ee0f6d49..cddf498cf50 100644
--- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp
+++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp
@@ -96,7 +96,7 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
     isToTaddr_ = false;
     isToZaddr_ = false;
 
-    std::visit(match {
+    examine(recipient.first, match {
         [&](const CKeyID& keyId) {
             toTaddr_ = keyId;
             isToTaddr_ = true;
@@ -118,7 +118,7 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
                     RPC_INVALID_ADDRESS_OR_KEY,
                     "z_mergetoaddress does not yet support sending to unified addresses");
         },
-    }, recipient.first);
+    });
 
     // Log the context info i.e. the call parameters to z_mergetoaddress
     if (LogAcceptCategory("zrpcunsafe")) {
diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp
index 407191914e3..01af895351b 100644
--- a/src/wallet/asyncrpcoperation_sendmany.cpp
+++ b/src/wallet/asyncrpcoperation_sendmany.cpp
@@ -151,7 +151,7 @@ uint256 AsyncRPCOperation_sendmany::main_impl(CWallet& wallet) {
             anchordepth_);
 
     uint256 txid;
-    std::visit(match {
+    examine(preparedTx, match {
         [&](const InputSelectionError& err) {
             ThrowInputSelectionError(err, ztxoSelector_, strategy_);
         },
@@ -194,7 +194,7 @@ uint256 AsyncRPCOperation_sendmany::main_impl(CWallet& wallet) {
                 throw;
             }
         }
-    }, preparedTx);
+    });
 
     return txid;
 }
diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp
index db255ccec8e..856fff5f833 100644
--- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp
+++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp
@@ -80,7 +80,7 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
     }
 
     //  Check the destination address is valid for this network i.e. not testnet being used on mainnet
-    std::visit(match {
+    examine(toAddress, match {
         [&](CKeyID addr) {
             throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2pkh address.");
         },
@@ -96,7 +96,7 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
         [&](libzcash::UnifiedAddress addr) {
             tozaddr_ = addr;
         }
-    }, toAddress);
+    });
 
     // Log the context info
     if (LogAcceptCategory("zrpcunsafe")) {
diff --git a/src/wallet/memo.h b/src/wallet/memo.h
index e4ee6c083ac..c95effbf9d2 100644
--- a/src/wallet/memo.h
+++ b/src/wallet/memo.h
@@ -50,7 +50,7 @@ class Memo {
     }
 
     static Memo FromHexOrThrow(const std::string& memoHex) {
-        return std::visit(match {
+        return examine(Memo::FromHex(memoHex), match {
             [&](Memo memo) {
                 return memo;
             },
@@ -69,7 +69,7 @@ class Memo {
                 // unreachable, but the compiler can't tell
                 return Memo::NoMemo();
             }
-        }, Memo::FromHex(memoHex));
+        });
     }
 
     // This copies, because if it returns a reference to the underlying value,
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 5355a730548..63dc344782e 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -929,7 +929,7 @@ UniValue z_exportkey(const UniValue& params, bool fHelp)
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
     }
 
-    std::string result = std::visit(match {
+    std::string result = examine(address.value(), match {
         [&](const CKeyID& addr) {
             CKey key;
             if (pwalletMain->GetKey(addr, key)) {
@@ -969,7 +969,7 @@ UniValue z_exportkey(const UniValue& params, bool fHelp)
                     "Use the emergency recovery phrase for this wallet for backup purposes instead.");
             return std::string(); //unreachable, here to make the compiler happy
         }
-    }, address.value());
+    });
     return result;
 }
 
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 9ca29d43010..ff4c4019025 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -526,19 +526,23 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
     std::set<CTxDestination> t_mnemonic_change_dests;
     std::set<CTxDestination> t_imported_dests;
     std::set<CTxDestination> t_watchonly_dests;
+
+    auto GetSourceForDestination = match {
+        [&](const CKeyID& addr) -> std::optional<PaymentAddressSource> {
+            return GetSourceForPaymentAddress(pwalletMain)(addr);
+        },
+        [&](const CScriptID& addr) -> std::optional<PaymentAddressSource> {
+            return GetSourceForPaymentAddress(pwalletMain)(addr);
+        },
+        [&](const CNoDestination& addr) -> std::optional<PaymentAddressSource> {
+            return std::nullopt;
+        },
+    };
+
     // Get the CTxDestination values for all the entries in the transparent address book.
     // This will include any address that has been generated by this wallet.
     for (const std::pair<CTxDestination, CAddressBookData>& item : pwalletMain->mapAddressBook) {
-        std::optional<PaymentAddressSource> source;
-        std::visit(match {
-            [&](const CKeyID& addr) {
-                source = GetSourceForPaymentAddress(pwalletMain)(addr);
-            },
-            [&](const CScriptID& addr) {
-                source = GetSourceForPaymentAddress(pwalletMain)(addr);
-            },
-            [&](const CNoDestination& addr) {}
-        }, item.first);
+        std::optional<PaymentAddressSource> source = std::visit(GetSourceForDestination, item.first);
         if (source.has_value()) {
             switch (source.value()) {
                 case PaymentAddressSource::Random:
@@ -570,16 +574,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
             t_imported_dests.count(item.first) == 0 &&
             t_watchonly_dests.count(item.first) == 0)
         {
-            std::optional<PaymentAddressSource> source;
-            std::visit(match {
-                [&](const CKeyID& addr) {
-                    source = GetSourceForPaymentAddress(pwalletMain)(addr);
-                },
-                [&](const CScriptID& addr) {
-                    source = GetSourceForPaymentAddress(pwalletMain)(addr);
-                },
-                [&](const CNoDestination& addr) {}
-            }, item.first);
+            std::optional<PaymentAddressSource> source = std::visit(GetSourceForDestination, item.first);
             if (source.has_value()) {
                 switch (source.value()) {
                     case PaymentAddressSource::Random:
@@ -3296,7 +3291,7 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp)
     UniValue result(UniValue::VOBJ);
     result.pushKV("account", (uint64_t)account);
 
-    std::visit(match {
+    examine(res, match {
         [&](std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t> addr) {
             result.pushKV("address", KeyIO(Params()).EncodePaymentAddress(addr.first));
             UniValue j;
@@ -3349,7 +3344,7 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp)
             }
             throw JSONRPCError(RPC_WALLET_ERROR, strErr);
         },
-    }, res);
+    });
 
     UniValue receiver_types(UniValue::VARR);
     for (const auto& receiverType : receiverTypes) {
@@ -3543,7 +3538,7 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp)
 
     UniValue result(UniValue::VOBJ);
     for (const auto& receiver : ua) {
-        std::visit(match {
+        examine(receiver, match {
             [&](const libzcash::OrchardRawAddress& addr) {
                 // Create a single-receiver UA that just contains this Orchard receiver.
                 UnifiedAddress singleReceiver;
@@ -3560,7 +3555,7 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp)
                 result.pushKV("p2pkh", keyIO.EncodePaymentAddress(addr));
             },
             [](auto rest) {},
-        }, receiver);
+        });
     }
     return result;
 }
@@ -3697,7 +3692,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
 
     // A non-unified address argument that is a receiver within a
     // unified address known to this wallet is not allowed.
-    if (std::visit(match {
+    if (examine(decoded.value(), match {
         [&](const CKeyID& addr) {
             return pwalletMain->FindUnifiedAddressByReceiver(addr).has_value();
          },
@@ -3715,7 +3710,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
             // We allow unified addresses themselves, which cannot recurse.
             return false;
         }
-    }, decoded.value())) {
+    })) {
         throw JSONRPCError(RPC_INVALID_PARAMETER, "The provided address is a bare receiver from a Unified Address in this wallet. Provide the full UA instead.");
     }
 
@@ -3820,7 +3815,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
         }
     };
 
-    std::visit(match {
+    examine(decoded.value(), match {
         [&](const CKeyID& addr) { push_transparent_result(addr); },
         [&](const CScriptID& addr) { push_transparent_result(addr); },
         [&](const libzcash::SproutPaymentAddress& addr) {
@@ -3857,7 +3852,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
         },
         [&](const libzcash::UnifiedAddress& addr) {
             for (const auto& receiver : addr) {
-                std::visit(match {
+                examine(receiver, match {
                     [&](const libzcash::SaplingPaymentAddress& addr) {
                         push_sapling_result(addr);
                     },
@@ -3874,10 +3869,10 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
                     },
                     [&](const UnknownReceiver& unknown) {}
 
-                }, receiver);
+                });
             }
         }
-    }, decoded.value());
+    });
     return result;
 }
 
@@ -3934,7 +3929,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
     }
 
     CAmount nBalance = 0;
-    std::visit(match {
+    examine(pa.value(), match {
         [&](const CKeyID& addr) {
             nBalance = getBalanceTaddr(addr, std::nullopt, nMinDepth, false);
         },
@@ -3966,7 +3961,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
                 nBalance += t.GetNoteValue();
             }
         },
-    }, pa.value());
+    });
 
     // inZat
     if (params.size() > 2 && params[2].get_bool()) {
@@ -4731,7 +4726,7 @@ size_t EstimateTxSize(
     size_t taddrRecipientCount = 0;
     size_t orchardRecipientCount = 0;
     for (const Payment& recipient : recipients) {
-        std::visit(match {
+        examine(recipient.GetAddress(), match {
             [&](const CKeyID&) {
                 taddrRecipientCount += 1;
             },
@@ -4756,7 +4751,7 @@ size_t EstimateTxSize(
                     taddrRecipientCount += 1;
                 }
             }
-        }, recipient.GetAddress());
+        });
     }
 
     bool nu5Active = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_NU5);
@@ -4905,7 +4900,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
         std::optional<Memo> memo;
         if (!memoValue.isNull()) {
             auto memoHex = memoValue.get_str();
-            std::visit(match {
+            examine(Memo::FromHex(memoHex), match {
                 [&](MemoError err) {
                     switch (err) {
                         case MemoError::HexDecodeError:
@@ -4923,7 +4918,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
                 [&](Memo result) {
                     memo = result;
                 }
-            }, Memo::FromHex(memoHex));
+            });
         }
 
         UniValue av = find_value(o, "amount");
@@ -4932,7 +4927,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive");
         }
 
-        std::visit(match {
+        examine(addr.value(), match {
             [&](const CKeyID &) {
                 tcoinbasePolicy = TransparentCoinbasePolicy::Disallow;
             },
@@ -4944,7 +4939,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
                 auto preferredRecipient =
                     ua.GetPreferredRecipientAddress(chainparams.GetConsensus(), nextBlockHeight);
                 if (preferredRecipient.has_value()) {
-                    std::visit(match {
+                    examine(preferredRecipient.value(), match {
                         [&](const CKeyID &) {
                             tcoinbasePolicy = TransparentCoinbasePolicy::Disallow;
                         },
@@ -4952,11 +4947,11 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
                             tcoinbasePolicy = TransparentCoinbasePolicy::Disallow;
                         },
                         [](const auto &) { }
-                    }, preferredRecipient.value());
+                    });
                 }
             },
             [](const auto &) { }
-        }, addr.value());
+        });
 
         recipients.push_back(Payment(addr.value(), nAmount, memo));
         nTotalOut += nAmount;
@@ -4993,7 +4988,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
 
             auto selectorAccount = pwalletMain->FindAccountForSelector(ztxoSelectorOpt.value());
             bool unknownOrLegacy = !selectorAccount.has_value() || selectorAccount.value() == ZCASH_LEGACY_ACCOUNT;
-            std::visit(match {
+            examine(decoded.value(), match {
                 [&](const libzcash::UnifiedAddress& ua) {
                     if (unknownOrLegacy) {
                         throw JSONRPCError(
@@ -5009,7 +5004,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
                                 "Invalid from address: is a bare receiver from a Unified Address in this wallet. Provide the UA as returned by z_getaddressforaccount instead.");
                     }
                 }
-            }, decoded.value());
+            });
 
             return ztxoSelectorOpt.value();
         }
@@ -5292,7 +5287,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
     auto destStr = params[1].get_str();
     auto destaddress = keyIO.DecodePaymentAddress(destStr);
     if (destaddress.has_value()) {
-        std::visit(match {
+        examine(destaddress.value(), match {
             [&](const CKeyID& addr) {
                 throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2pkh address.");
             },
@@ -5316,7 +5311,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
                 }
                 involvesOrchard = ua.GetOrchardReceiver().has_value();
             }
-        }, destaddress.value());
+        });
     } else {
         throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destStr);
     }
@@ -5570,7 +5565,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
         } else {
             auto addr = keyIO.DecodePaymentAddress(address);
             if (addr.has_value()) {
-                std::visit(match {
+                examine(addr.value(), match {
                     [&](const CKeyID& taddr) {
                         taddrs.insert(taddr);
                         isFromNonSprout = true;
@@ -5591,7 +5586,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
                                 RPC_INVALID_PARAMETER,
                                 "Unified addresses are not supported in z_mergetoaddress");
                     }
-                }, addr.value());
+                });
             } else {
                 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Unknown address format: ") + address);
             }
@@ -5621,7 +5616,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
     bool isToSproutZaddr = false;
     bool isToSaplingZaddr = false;
     if (destaddress.has_value()) {
-        std::visit(match {
+        examine(destaddress.value(), match {
             [&](CKeyID addr) {
                 isToTaddr = true;
             },
@@ -5643,7 +5638,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
                         RPC_INVALID_PARAMETER,
                         "Invalid parameter, unified addresses are not yet supported.");
             }
-        }, destaddress.value());
+        });
     } else {
         throw JSONRPCError(
                 RPC_INVALID_PARAMETER,
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6179c873629..451de78896a 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -680,14 +680,14 @@ std::optional<ZcashdUnifiedFullViewingKey> CWallet::GetUnifiedFullViewingKeyByAc
 }
 
 WalletUAGenerationResult ToWalletUAGenerationResult(UnifiedAddressGenerationResult result) {
-    return std::visit(match {
+    return examine(result, match {
         [](const UnifiedAddressGenerationError& err) {
             return WalletUAGenerationResult(err);
         },
         [](const std::pair<UnifiedAddress, diversifier_index_t>& addrPair) {
             return WalletUAGenerationResult(addrPair);
         }
-    }, result);
+    });
 }
 
 WalletUAGenerationResult CWallet::GenerateUnifiedAddress(
@@ -882,7 +882,7 @@ std::pair<PaymentAddress, RecipientType> CWallet::GetPaymentAddressForRecipient(
     }
 
     auto ufvk = self->GetUFVKForReceiver(RecipientAddressToReceiver(recipient));
-    std::pair<PaymentAddress, RecipientType> defaultAddress = std::visit(match {
+    std::pair<PaymentAddress, RecipientType> defaultAddress = examine(recipient, match {
         [&](const CKeyID& addr) {
             auto ua = self->FindUnifiedAddressByReceiver(addr);
             if (ua.has_value()) {
@@ -979,7 +979,7 @@ std::pair<PaymentAddress, RecipientType> CWallet::GetPaymentAddressForRecipient(
 
             return std::make_pair(PaymentAddress{UnifiedAddress::ForSingleReceiver(addr)}, RecipientType::CounterpartyAddress);
         }
-    }, recipient);
+    });
 
     auto recipientsPtr = sendRecipients.find(txid);
     if (recipientsPtr == sendRecipients.end()) {
@@ -1005,7 +1005,7 @@ std::pair<PaymentAddress, RecipientType> CWallet::GetPaymentAddressForRecipient(
 bool CWallet::IsInternalRecipient(const libzcash::RecipientAddress& recipient) const
 {
     auto self = this;
-    return std::visit(match {
+    return examine(recipient, match {
         [&](const CKeyID& addr) {
             // we never send transparent change when sending to or from a
             // unified address
@@ -1035,7 +1035,7 @@ bool CWallet::IsInternalRecipient(const libzcash::RecipientAddress& recipient) c
             }
             return false;
         }
-    }, recipient);
+    });
 }
 
 void CWallet::LoadRecipientMapping(const uint256& txid, const RecipientMapping& mapping) {
@@ -1092,7 +1092,7 @@ bool CWallet::LoadCaches()
                 // restore unified addresses that have been previously generated to the
                 // keystore
                 for (const auto &[j, receiverTypes] : metadata->second.GetKnownReceiverSetsByDiversifierIndex()) {
-                    bool restored = std::visit(match {
+                    bool restored = examine(ufvk.value().Address(j, receiverTypes), match {
                         [&](const UnifiedAddressGenerationError& err) {
                             LogPrintf("%s: Error: Unable to generate a unified address.\n",
                                 __func__);
@@ -1115,7 +1115,7 @@ bool CWallet::LoadCaches()
                             return CCryptoKeyStore::AddTransparentReceiverForUnifiedAddress(
                                     ufvkId, addr.second, addr.first);
                         }
-                    }, ufvk.value().Address(j, receiverTypes));
+                    });
 
                     // failure to restore the generated address is an error
                     if (!restored) return false;
@@ -1896,7 +1896,7 @@ std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAddress(
 {
     auto self = this;
     std::optional<ZTXOPattern> pattern = std::nullopt;
-    std::visit(match {
+    examine(addr, match {
         [&](const CKeyID& addr) {
             if (!requireSpendingKey || self->HaveKey(addr)) {
                 pattern = addr;
@@ -1935,7 +1935,7 @@ std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAddress(
                 }
             }
         }
-    }, addr);
+    });
 
     if (pattern.has_value()) {
         return ZTXOSelector(pattern.value(), requireSpendingKey, transparentCoinbasePolicy);
@@ -1951,7 +1951,7 @@ std::optional<ZTXOSelector> CWallet::ZTXOSelectorForViewingKey(
 {
     auto self = this;
     std::optional<ZTXOPattern> pattern = std::nullopt;
-    std::visit(match {
+    examine(vk, match {
         [&](const libzcash::SaplingExtendedFullViewingKey& vk) {
             if (!requireSpendingKey || self->HaveSaplingSpendingKey(vk)) {
                 pattern = vk;
@@ -1971,7 +1971,7 @@ std::optional<ZTXOSelector> CWallet::ZTXOSelectorForViewingKey(
                 pattern = ufvk;
             }
         }
-    }, vk);
+    });
 
     if (pattern.has_value()) {
         return ZTXOSelector(pattern.value(), requireSpendingKey, transparentCoinbasePolicy);
@@ -1990,7 +1990,7 @@ ZTXOSelector CWallet::LegacyTransparentZTXOSelector(bool requireSpendingKey, Tra
 std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSelector& selector) const {
     auto self = this;
     std::optional<libzcash::AccountId> result{};
-    std::visit(match {
+    examine(selector.GetPattern(), match {
         [&](const CKeyID& addr) {
             auto meta = self->GetUFVKMetadataForReceiver(addr);
             if (meta.has_value()) {
@@ -2034,7 +2034,7 @@ std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSel
                 result = acct.GetAccountId();
             }
         }
-    }, selector.GetPattern());
+    });
     return result;
 }
 
@@ -2044,7 +2044,7 @@ bool CWallet::SelectorMatchesAddress(
         const ZTXOSelector& selector,
         const CTxDestination& address) const {
     auto self = this;
-    return std::visit(match {
+    return examine(selector.GetPattern(), match {
         [&](const CKeyID& keyId) {
             CTxDestination keyIdDest = keyId;
             return address == keyIdDest;
@@ -2061,7 +2061,7 @@ bool CWallet::SelectorMatchesAddress(
             // for a UA selector when matching transparent addresses, we only match addresses
             // that explicitly appear as receivers in the UA.
             for (const auto& receiver : uaSelector) {
-                bool matches = std::visit(match {
+                bool matches = examine(receiver, match {
                     [&](const libzcash::OrchardRawAddress& orchardAddr) { return false; },
                     [&](const libzcash::SaplingPaymentAddress& saplingAddr) { return false; },
                     [&](const libzcash::UnknownReceiver& receiver) { return false; },
@@ -2073,7 +2073,7 @@ bool CWallet::SelectorMatchesAddress(
                         CTxDestination keyIdDest = keyId;
                         return address == keyIdDest;
                     }
-                }, receiver);
+                });
                 if (matches) return true;
             }
             return false;
@@ -2098,24 +2098,24 @@ bool CWallet::SelectorMatchesAddress(
             }
             return false;
         }
-    }, selector.GetPattern());
+    });
 }
 // Sprout
 bool CWallet::SelectorMatchesAddress(
         const ZTXOSelector& selector,
         const libzcash::SproutPaymentAddress& a0) const {
-    return std::visit(match {
+    return examine(selector.GetPattern(), match {
         [&](const libzcash::SproutPaymentAddress& a1) { return a0 == a1; },
         [&](const libzcash::SproutViewingKey& vk) { return a0 == vk.address(); },
         [&](const auto& addr) { return false; },
-    }, selector.GetPattern());
+    });
 }
 // Sapling
 bool CWallet::SelectorMatchesAddress(
         const ZTXOSelector& selector,
         const libzcash::SaplingPaymentAddress& a0) const {
     auto self = this;
-    return std::visit(match {
+    return examine(selector.GetPattern(), match {
         [&](const CKeyID& keyId) { return false; },
         [&](const CScriptID& scriptId) { return false; },
         [&](const libzcash::SproutPaymentAddress& addr) { return false; },
@@ -2162,7 +2162,7 @@ bool CWallet::SelectorMatchesAddress(
             }
             return false;
         }
-    }, selector.GetPattern());
+    });
 }
 
 std::optional<RecipientAddress> CWallet::GenerateChangeAddressForAccount(
@@ -2357,7 +2357,7 @@ SpendableInputs CWallet::FindSpendableInputs(
 
     if (selectOrchard) {
         // for Orchard, we select both the internal and external IVKs.
-        auto orchardIvks = std::visit(match {
+        auto orchardIvks = examine(selector.GetPattern(), match {
             [&](const libzcash::UnifiedAddress& selectorUA) -> std::vector<OrchardIncomingViewingKey> {
                 auto orchardReceiver = selectorUA.GetOrchardReceiver();
                 if (orchardReceiver.has_value()) {
@@ -2392,7 +2392,7 @@ SpendableInputs CWallet::FindSpendableInputs(
                 return {};
             },
             [&](const auto& addr) -> std::vector<OrchardIncomingViewingKey> { return {}; }
-        }, selector.GetPattern());
+        });
 
         for (const auto& ivk : orchardIvks) {
             std::vector<OrchardNoteMetadata> incomingNotes;
@@ -3993,7 +3993,7 @@ bool CWallet::IsChange(const CTxOut& txout) const
     // We look to key metadata to determine whether the address was generated
     // using an internal key path. This could fail to identify some legacy
     // change addresses as change outputs.
-    return std::visit(match {
+    return examine(address, match {
         [&](const CKeyID& key) {
             auto keyMetaIt = mapKeyMetadata.find(key);
             return
@@ -4006,7 +4006,7 @@ bool CWallet::IsChange(const CTxOut& txout) const
                 IsInternalKeyPath(44, BIP44CoinType(), keyMetaIt->second.hdKeypath);
         },
         [&](const auto& other) { return false; }
-    }, address);
+    });
 }
 
 CAmount CWallet::GetChange(const CTxOut& txout) const
@@ -6905,7 +6905,7 @@ bool CMerkleTx::AcceptToMemoryPool(CValidationState& state, bool fLimitFree, boo
 NoteFilter NoteFilter::ForPaymentAddresses(const std::vector<libzcash::PaymentAddress>& paymentAddrs) {
     NoteFilter addrs;
     for (const auto& addr: paymentAddrs) {
-        std::visit(match {
+        examine(addr, match {
             [&](const CKeyID& keyId) { },
             [&](const CScriptID& scriptId) { },
             [&](const libzcash::SproutPaymentAddress& addr) {
@@ -6916,7 +6916,7 @@ NoteFilter NoteFilter::ForPaymentAddresses(const std::vector<libzcash::PaymentAd
             },
             [&](const libzcash::UnifiedAddress& uaddr) {
                 for (auto& receiver : uaddr) {
-                    std::visit(match {
+                    examine(receiver, match {
                         [&](const libzcash::OrchardRawAddress& addr) {
                             addrs.orchardAddresses.insert(addr);
                         },
@@ -6924,10 +6924,10 @@ NoteFilter NoteFilter::ForPaymentAddresses(const std::vector<libzcash::PaymentAd
                             addrs.saplingAddresses.insert(addr);
                         },
                         [&](const auto& other) { }
-                    }, receiver);
+                    });
                 }
             },
-        }, addr);
+        });
     }
     return addrs;
 }
@@ -7735,7 +7735,7 @@ bool TransactionStrategy::IsCompatibleWith(PrivacyPolicy policy) const {
 }
 
 bool ZTXOSelector::SelectsTransparent() const {
-    return std::visit(match {
+    return examine(this->pattern, match {
         [](const CKeyID& keyId) { return true; },
         [](const CScriptID& scriptId) { return true; },
         [](const libzcash::SproutPaymentAddress& addr) { return false; },
@@ -7747,32 +7747,32 @@ bool ZTXOSelector::SelectsTransparent() const {
         },
         [](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetTransparentKey().has_value(); },
         [](const AccountZTXOPattern& acct) { return acct.IncludesP2PKH() || acct.IncludesP2SH(); }
-    }, this->pattern);
+    });
 }
 bool ZTXOSelector::SelectsSprout() const {
-    return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && std::visit(match {
+    return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && examine(this->pattern, match {
         [](const libzcash::SproutViewingKey& addr) { return true; },
         [](const libzcash::SproutPaymentAddress& extfvk) { return true; },
         [](const auto& addr) { return false; }
-    }, this->pattern);
+    });
 }
 bool ZTXOSelector::SelectsSapling() const {
-    return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && std::visit(match {
+    return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && examine(this->pattern, match {
         [](const libzcash::SaplingPaymentAddress& addr) { return true; },
         [](const libzcash::SaplingExtendedSpendingKey& extfvk) { return true; },
         [](const libzcash::UnifiedAddress& ua) { return ua.GetSaplingReceiver().has_value(); },
         [](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetSaplingKey().has_value(); },
         [](const AccountZTXOPattern& acct) { return acct.IncludesSapling(); },
         [](const auto& addr) { return false; }
-    }, this->pattern);
+    });
 }
 bool ZTXOSelector::SelectsOrchard() const {
-    return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && std::visit(match {
+    return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && examine(this->pattern, match {
         [](const libzcash::UnifiedAddress& ua) { return ua.GetOrchardReceiver().has_value(); },
         [](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetOrchardKey().has_value(); },
         [](const AccountZTXOPattern& acct) { return acct.IncludesOrchard(); },
         [](const auto& addr) { return false; }
-    }, this->pattern);
+    });
 }
 
 bool SpendableInputs::LimitToAmount(
diff --git a/src/wallet/wallet_tx_builder.cpp b/src/wallet/wallet_tx_builder.cpp
index 6e7cce64ab3..332f1b7c139 100644
--- a/src/wallet/wallet_tx_builder.cpp
+++ b/src/wallet/wallet_tx_builder.cpp
@@ -105,7 +105,7 @@ PrepareTransactionResult WalletTxBuilder::PrepareTransaction(
             return addChangePayment(zufvk.GetChangeAddress(getAllowedChangePools(receiverTypes)));
         };
 
-        changeAddr = std::visit(match {
+        changeAddr = examine(selector.GetPattern(), match {
             [&](const CKeyID&) -> ChangeAddress {
                 return changeAddressForTransparentSelector({ReceiverType::P2PKH});
             },
@@ -142,7 +142,7 @@ PrepareTransactionResult WalletTxBuilder::PrepareTransaction(
                                 acct.GetAccountId(),
                                 getAllowedChangePools(acct.GetReceiverTypes())));
             }
-        }, selector.GetPattern());
+        });
     }
 
     auto ovks = SelectOVKs(wallet, selector, spendable);
@@ -216,7 +216,7 @@ InputSelectionResult WalletTxBuilder::ResolveInputsAndPayments(
     std::vector<ResolvedPayment> resolvedPayments;
     std::optional<AddressResolutionError> resolutionError;
     for (const auto& payment : payments) {
-        std::visit(match {
+        examine(payment.GetAddress(), match {
             [&](const CKeyID& p2pkh) {
                 if (strategy.AllowRevealedRecipients()) {
                     resolvedPayments.emplace_back(
@@ -289,7 +289,7 @@ InputSelectionResult WalletTxBuilder::ResolveInputsAndPayments(
                     }
                 }
             }
-        }, payment.GetAddress());
+        });
 
         if (resolutionError.has_value()) {
             return resolutionError.value();
@@ -379,7 +379,7 @@ std::pair<uint256, uint256> WalletTxBuilder::SelectOVKs(
         const ZTXOSelector& selector,
         const SpendableInputs& spendable) const
 {
-    return std::visit(match {
+    return examine(selector.GetPattern(), match {
         [&](const CKeyID& keyId) {
             return wallet.GetLegacyAccountKey().ToAccountPubKey().GetOVKsForShielding();
         },
@@ -420,7 +420,7 @@ std::pair<uint256, uint256> WalletTxBuilder::SelectOVKs(
                 return GetOVKsForUFVK(ufvk.value().ToFullViewingKey(), spendable);
             }
         },
-    }, selector.GetPattern());
+    });
 }
 
 PrivacyPolicy TransactionEffects::GetRequiredPrivacyPolicy() const
@@ -550,39 +550,37 @@ TransactionBuilderResult TransactionEffects::ApproveAndBuild(
     }
 
     // Add outputs
-    try {
-        for (const auto& r : payments.GetResolvedPayments()) {
-            std::visit(match {
-                    [&](const CKeyID& keyId) {
-                        if (r.memo.has_value()) {
-                            throw TransactionBuilderResult(
-                                    "Memos cannot be sent to transparent addresses.");
-                        } else {
-                            builder.AddTransparentOutput(keyId, r.amount);
-                        }
-                    },
-                    [&](const CScriptID& scriptId) {
-                        if (r.memo.has_value()) {
-                            throw TransactionBuilderResult(
-                                    "Memos cannot be sent to transparent addresses.");
-                        } else {
-                            builder.AddTransparentOutput(scriptId, r.amount);
-                        }
-                    },
-                    [&](const libzcash::SaplingPaymentAddress& addr) {
-                        builder.AddSaplingOutput(
-                                r.isInternal ? internalOVK : externalOVK, addr, r.amount,
-                                r.memo.has_value() ? r.memo.value().ToBytes() : Memo::NoMemo().ToBytes());
-                    },
-                    [&](const libzcash::OrchardRawAddress& addr) {
-                        builder.AddOrchardOutput(
-                                r.isInternal ? internalOVK : externalOVK, addr, r.amount,
-                                r.memo.has_value() ? std::optional(r.memo.value().ToBytes()) : std::nullopt);
-                    }
-                    }, r.address);
+    for (const auto& r : payments.GetResolvedPayments()) {
+        std::optional<TransactionBuilderResult> result;
+        examine(r.address, match {
+            [&](const CKeyID& keyId) {
+                if (r.memo.has_value()) {
+                    result = TransactionBuilderResult("Memos cannot be sent to transparent addresses.");
+                } else {
+                    builder.AddTransparentOutput(keyId, r.amount);
+                }
+            },
+            [&](const CScriptID& scriptId) {
+                if (r.memo.has_value()) {
+                    result = TransactionBuilderResult("Memos cannot be sent to transparent addresses.");
+                } else {
+                    builder.AddTransparentOutput(scriptId, r.amount);
+                }
+            },
+            [&](const libzcash::SaplingPaymentAddress& addr) {
+                builder.AddSaplingOutput(
+                        r.isInternal ? internalOVK : externalOVK, addr, r.amount,
+                        r.memo.has_value() ? r.memo.value().ToBytes() : Memo::NoMemo().ToBytes());
+            },
+            [&](const libzcash::OrchardRawAddress& addr) {
+                builder.AddOrchardOutput(
+                        r.isInternal ? internalOVK : externalOVK, addr, r.amount,
+                        r.memo.has_value() ? std::optional(r.memo.value().ToBytes()) : std::nullopt);
+            },
+        });
+        if (result.has_value()) {
+            return result.value();
         }
-    } catch (const TransactionBuilderResult& result) {
-        return result;
     }
 
     // Add transparent utxos
@@ -635,14 +633,14 @@ TransactionBuilderResult TransactionEffects::ApproveAndBuild(
     //       (re)calculate the change. In future, we shouldn’t rely on `TransactionBuilder` ever
     //       calculating change.
     if (changeAddr.has_value()) {
-        std::visit(match {
+        examine(changeAddr.value(), match {
             [&](const SproutPaymentAddress& addr) {
                 builder.SendChangeToSprout(addr);
             },
             [&](const RecipientAddress&) {
                 assert(totalSpend == payments.Total() + fee);
             }
-        }, changeAddr.value());
+        });
     }
 
     // Build the transaction
diff --git a/src/wallet/wallet_tx_builder.h b/src/wallet/wallet_tx_builder.h
index ac5ff34bc51..cb1b5441c07 100644
--- a/src/wallet/wallet_tx_builder.h
+++ b/src/wallet/wallet_tx_builder.h
@@ -80,7 +80,7 @@ class Payments {
     }
 
     void AddPayment(ResolvedPayment payment) {
-        std::visit(match {
+        examine(payment.address, match {
             [&](const CKeyID& addr) {
                 t_outputs_total += payment.amount;
                 recipientPools.insert(OutputPool::Transparent);
@@ -97,7 +97,7 @@ class Payments {
                 orchard_outputs_total += payment.amount;
                 recipientPools.insert(OutputPool::Orchard);
             }
-        }, payment.address);
+        });
         payments.push_back(payment);
     }
 
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 096ff6d3cc6..e05930f8b5e 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -336,7 +336,7 @@ class CSerializeRecipientAddress {
 
         template<typename Stream>
         void Serialize(Stream& s) const {
-            std::visit(match {
+            examine(recipient, match {
                 [&](const CKeyID& keyId) {
                     ReceiverTypeSer(libzcash::ReceiverType::P2PKH).Serialize(s);
                     s << keyId;
@@ -353,7 +353,7 @@ class CSerializeRecipientAddress {
                     ReceiverTypeSer(libzcash::ReceiverType::Orchard).Serialize(s);
                     s << orchardAddr;
                 }
-            }, recipient);
+            });
         }
 
         template<typename Stream>
diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp
index 3b98c446afe..dc7a6fb98ce 100644
--- a/src/zcash/Address.cpp
+++ b/src/zcash/Address.cpp
@@ -178,13 +178,13 @@ std::optional<RecipientAddress> UnifiedAddress::GetPreferredRecipientAddress(
     // order from most-preferred to least.
     std::optional<RecipientAddress> result;
     for (const auto& receiver : *this) {
-        std::visit(match {
+        examine(receiver, match {
             [&](const OrchardRawAddress& addr) { if (nu5Active) result = addr; },
             [&](const SaplingPaymentAddress& addr) { result = addr; },
             [&](const CScriptID& addr) { result = addr; },
             [&](const CKeyID& addr) { result = addr; },
             [&](const UnknownReceiver& addr) { }
-        }, receiver);
+        });
 
         if (result.has_value()) {
             return result;
@@ -194,13 +194,13 @@ std::optional<RecipientAddress> UnifiedAddress::GetPreferredRecipientAddress(
 }
 
 bool HasKnownReceiverType(const Receiver& receiver) {
-    return std::visit(match {
+    return examine(receiver, match {
         [](const OrchardRawAddress& addr) { return true; },
         [](const SaplingPaymentAddress& addr) { return true; },
         [](const CScriptID& addr) { return true; },
         [](const CKeyID& addr) { return true; },
         [](const UnknownReceiver& addr) { return false; }
-    }, receiver);
+    });
 }
 
 std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const SproutSpendingKey &sk) const {
diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp
index 4b8eedae0e4..52a5c4de03b 100644
--- a/src/zcash/Address.hpp
+++ b/src/zcash/Address.hpp
@@ -102,7 +102,7 @@ class UnifiedAddress {
     std::set<ReceiverType> GetKnownReceiverTypes() const {
         std::set<ReceiverType> result;
         for (const auto& receiver : receivers) {
-            std::visit(match {
+            examine(receiver, match {
                 [&](const libzcash::OrchardRawAddress &zaddr) {
                     result.insert(ReceiverType::Orchard);
                 },
@@ -117,7 +117,7 @@ class UnifiedAddress {
                 },
                 [&](const libzcash::UnknownReceiver &uaddr) {
                 }
-            }, receiver);
+            });
         }
         return result;
     }
diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp
index aaa9fc3cc6a..4b26497d365 100644
--- a/src/zcash/address/unified.cpp
+++ b/src/zcash/address/unified.cpp
@@ -33,16 +33,16 @@ bool libzcash::HasTransparent(const std::set<ReceiverType>& receiverTypes) {
 }
 
 Receiver libzcash::RecipientAddressToReceiver(const RecipientAddress& recipient) {
-    return std::visit(match {
+    return examine(recipient, match {
         [](const CKeyID& key) { return Receiver(key); },
         [](const CScriptID& scriptId) { return Receiver(scriptId); },
         [](const libzcash::OrchardRawAddress& addr) { return Receiver(addr); },
         [](const libzcash::SaplingPaymentAddress& addr) { return Receiver(addr); }
-    }, recipient);
+    });
 }
 
 std::string libzcash::DebugPrintReceiver(const Receiver& receiver) {
-    return std::visit(match {
+    return examine(receiver, match {
         [&](const OrchardRawAddress &zaddr) {
             CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
             ss << zaddr;
@@ -65,7 +65,7 @@ std::string libzcash::DebugPrintReceiver(const Receiver& receiver) {
                 unknown.typecode,
                 HexStr(unknown.data.begin(), unknown.data.end()));
         }
-    }, receiver);
+    });
 };
 
 std::string libzcash::DebugPrintRecipientAddress(const RecipientAddress& addr) {
@@ -198,7 +198,7 @@ UnifiedAddressGenerationResult ZcashdUnifiedFullViewingKey::FindAddress(
 
 std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress(const ChangeRequest& req) const {
     std::optional<RecipientAddress> addr;
-    std::visit(match {
+    examine(req, match {
         [&](const TransparentChangeRequest& req) {
             if (transparentKey.has_value()) {
                 auto changeAddr = transparentKey.value().GetChangeAddress(req.GetIndex());
@@ -217,7 +217,7 @@ std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress(co
                 addr = orchardKey.value().GetChangeAddress();
             }
         }
-    }, req);
+    });
     return addr;
 }
 


Test patch: