diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -911,6 +911,10 @@
         return ValueFromAmount(pwallet->GetBalance());
     }
 
+    const std::string *account = request.params[0].get_str() != "*"
+                                     ? &request.params[0].get_str()
+                                     : nullptr;
+
     int nMinDepth = 1;
     if (request.params.size() > 1) {
         nMinDepth = request.params[1].get_int();
@@ -921,49 +925,8 @@
         filter = filter | ISMINE_WATCH_ONLY;
     }
 
-    if (request.params[0].get_str() == "*") {
-        // Calculate total balance in a very different way from GetBalance().
-        // The biggest difference is that GetBalance() sums up all unspent
-        // TxOuts paying to the wallet, while this sums up both spent and
-        // unspent TxOuts paying to the wallet, and then subtracts the values of
-        // TxIns spending from the wallet. This also has fewer restrictions on
-        // which unconfirmed transactions are considered trusted.
-        Amount nBalance(0);
-        for (const std::pair<uint256, CWalletTx> &pairWtx :
-             pwallet->mapWallet) {
-            const CWalletTx &wtx = pairWtx.second;
-            CValidationState state;
-            if (!ContextualCheckTransactionForCurrentBlock(config, wtx,
-                                                           state) ||
-                wtx.GetBlocksToMaturity() > 0 ||
-                wtx.GetDepthInMainChain() < 0) {
-                continue;
-            }
-
-            Amount allFee;
-            std::string strSentAccount;
-            std::list<COutputEntry> listReceived;
-            std::list<COutputEntry> listSent;
-            wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount,
-                           filter);
-            if (wtx.GetDepthInMainChain() >= nMinDepth) {
-                for (const COutputEntry &r : listReceived) {
-                    nBalance += r.amount;
-                }
-            }
-            for (const COutputEntry &s : listSent) {
-                nBalance -= s.amount;
-            }
-            nBalance -= allFee;
-        }
-        return ValueFromAmount(nBalance);
-    }
-
-    std::string strAccount = AccountFromValue(request.params[0]);
-
-    Amount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, filter);
-
-    return ValueFromAmount(nBalance);
+    return ValueFromAmount(
+        pwallet->GetLegacyBalance(filter, nMinDepth, account));
 }
 
 static UniValue getunconfirmedbalance(const Config &config,
@@ -1147,7 +1110,7 @@
 
     // Check funds
     Amount nBalance =
-        pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
+        pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
     if (nAmount > nBalance) {
         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
                            "Account has insufficient funds");
@@ -1307,7 +1270,7 @@
 
     // Check funds
     Amount nBalance =
-        pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
+        pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
     if (totalAmount > nBalance) {
         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
                            "Account has insufficient funds");
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -436,10 +436,6 @@
                     std::string &strSentAccount,
                     const isminefilter &filter) const;
 
-    void GetAccountAmounts(const std::string &strAccount, Amount &nReceived,
-                           Amount &nSent, Amount &nFee,
-                           const isminefilter &filter) const;
-
     bool IsFromMe(const isminefilter &filter) const {
         return (GetDebit(filter) > Amount(0));
     }
@@ -899,6 +895,8 @@
     Amount GetWatchOnlyBalance() const;
     Amount GetUnconfirmedWatchOnlyBalance() const;
     Amount GetImmatureWatchOnlyBalance() const;
+    Amount GetLegacyBalance(const isminefilter &filter, int minDepth,
+                            const std::string *account) const;
 
     /**
      * Insert additional inputs into the transaction by calling
@@ -979,10 +977,6 @@
     std::set<std::set<CTxDestination>> GetAddressGroupings();
     std::map<CTxDestination, Amount> GetAddressBalances();
 
-    Amount GetAccountBalance(const std::string &strAccount, int nMinDepth,
-                             const isminefilter &filter);
-    Amount GetAccountBalance(CWalletDB &walletdb, const std::string &strAccount,
-                             int nMinDepth, const isminefilter &filter);
     std::set<CTxDestination>
     GetAccountAddresses(const std::string &strAccount) const;
 
@@ -1016,6 +1010,8 @@
 
     bool DelAddressBook(const CTxDestination &address);
 
+    const std::string &GetAccountName(const CScript &scriptPubKey) const;
+
     void Inventory(const uint256 &hash) override {
         LOCK(cs_wallet);
         std::map<uint256, int>::iterator mi = mapRequestCount.find(hash);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1696,40 +1696,6 @@
     }
 }
 
-void CWalletTx::GetAccountAmounts(const std::string &strAccount,
-                                  Amount &nReceived, Amount &nSent,
-                                  Amount &nFee,
-                                  const isminefilter &filter) const {
-    nReceived = nSent = nFee = Amount(0);
-
-    Amount allFee;
-    std::string strSentAccount;
-    std::list<COutputEntry> listReceived;
-    std::list<COutputEntry> listSent;
-    GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
-
-    if (strAccount == strSentAccount) {
-        for (const COutputEntry &s : listSent) {
-            nSent += s.amount;
-        }
-        nFee = allFee;
-    }
-
-    LOCK(pwallet->cs_wallet);
-    for (const COutputEntry &r : listReceived) {
-        if (pwallet->mapAddressBook.count(r.destination)) {
-            std::map<CTxDestination, CAddressBookData>::const_iterator mi =
-                pwallet->mapAddressBook.find(r.destination);
-            if (mi != pwallet->mapAddressBook.end() &&
-                (*mi).second.name == strAccount) {
-                nReceived += r.amount;
-            }
-        } else if (strAccount.empty()) {
-            nReceived += r.amount;
-        }
-    }
-}
-
 /**
  * Scan the block chain (starting in pindexStart) for transactions from or to
  * us. If fUpdate is true, found transactions that already exist in the wallet
@@ -2251,6 +2217,52 @@
     return nTotal;
 }
 
+// Calculate total balance in a different way from GetBalance. The biggest
+// difference is that GetBalance sums up all unspent TxOuts paying to the
+// wallet, while this sums up both spent and unspent TxOuts paying to the
+// wallet, and then subtracts the values of TxIns spending from the wallet. This
+// also has fewer restrictions on which unconfirmed transactions are considered
+// trusted.
+Amount CWallet::GetLegacyBalance(const isminefilter &filter, int minDepth,
+                                 const std::string *account) const {
+    LOCK2(cs_main, cs_wallet);
+
+    Amount balance(0);
+    for (const auto &entry : mapWallet) {
+        const CWalletTx &wtx = entry.second;
+        const int depth = wtx.GetDepthInMainChain();
+        if (depth < 0 || !CheckFinalTx(*wtx.tx) ||
+            wtx.GetBlocksToMaturity() > 0) {
+            continue;
+        }
+
+        // Loop through tx outputs and add incoming payments. For outgoing txs,
+        // treat change outputs specially, as part of the amount debited.
+        Amount debit = wtx.GetDebit(filter);
+        const bool outgoing = debit > Amount(0);
+        for (const CTxOut &out : wtx.tx->vout) {
+            if (outgoing && IsChange(out)) {
+                debit -= out.nValue;
+            } else if (IsMine(out) & filter && depth >= minDepth &&
+                       (!account ||
+                        *account == GetAccountName(out.scriptPubKey))) {
+                balance += out.nValue;
+            }
+        }
+
+        // For outgoing txs, subtract amount debited.
+        if (outgoing && (!account || *account == wtx.strFromAccount)) {
+            balance -= debit;
+        }
+    }
+
+    if (account) {
+        balance += CWalletDB(*dbw).GetAccountCreditDebit(*account);
+    }
+
+    return balance;
+}
+
 void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe,
                              const CCoinControl *coinControl,
                              bool fIncludeZeroValue) const {
@@ -3298,6 +3310,21 @@
     return CWalletDB(*dbw).EraseName(address);
 }
 
+const std::string &CWallet::GetAccountName(const CScript &scriptPubKey) const {
+    CTxDestination address;
+    if (ExtractDestination(scriptPubKey, address) &&
+        !scriptPubKey.IsUnspendable()) {
+        auto mi = mapAddressBook.find(address);
+        if (mi != mapAddressBook.end()) {
+            return mi->second.name;
+        }
+    }
+    // A scriptPubKey that doesn't have an entry in the address book is
+    // associated with the default account ("").
+    const static std::string DEFAULT_ACCOUNT_NAME;
+    return DEFAULT_ACCOUNT_NAME;
+}
+
 /**
  * Mark old keypool keys as used, and generate all new keys.
  */
@@ -3677,42 +3704,6 @@
     return ret;
 }
 
-Amount CWallet::GetAccountBalance(const std::string &strAccount, int nMinDepth,
-                                  const isminefilter &filter) {
-    CWalletDB walletdb(*dbw);
-    return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
-}
-
-Amount CWallet::GetAccountBalance(CWalletDB &walletdb,
-                                  const std::string &strAccount, int nMinDepth,
-                                  const isminefilter &filter) {
-    Amount nBalance(0);
-
-    // Tally wallet transactions.
-    for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin();
-         it != mapWallet.end(); ++it) {
-        const CWalletTx &wtx = (*it).second;
-        if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 ||
-            wtx.GetDepthInMainChain() < 0) {
-            continue;
-        }
-
-        Amount nReceived, nSent, nFee;
-        wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter);
-
-        if (nReceived != Amount(0) && wtx.GetDepthInMainChain() >= nMinDepth) {
-            nBalance += nReceived;
-        }
-
-        nBalance -= nSent + nFee;
-    }
-
-    // Tally internal accounting entries.
-    nBalance += walletdb.GetAccountCreditDebit(strAccount);
-
-    return nBalance;
-}
-
 std::set<CTxDestination>
 CWallet::GetAccountAddresses(const std::string &strAccount) const {
     LOCK(cs_wallet);