diff --git a/chain/bitcoind_client.go b/chain/bitcoind_client.go
index 4082b729df..a2537da617 100644
--- a/chain/bitcoind_client.go
+++ b/chain/bitcoind_client.go
@@ -1400,3 +1400,11 @@ func (c *BitcoindClient) filterTx(txDetails *btcutil.Tx,
 
 	return true, rec, nil
 }
+
+// LookupInputMempoolSpend returns the transaction hash and true if the given
+// input is found being spent in mempool, otherwise it returns nil and false.
+func (c *BitcoindClient) LookupInputMempoolSpend(op wire.OutPoint) (
+	chainhash.Hash, bool) {
+
+	return c.chainConn.events.LookupInputSpend(op)
+}
diff --git a/chain/bitcoind_events_test.go b/chain/bitcoind_events_test.go
index fc9f8b8f8c..3056cc495d 100644
--- a/chain/bitcoind_events_test.go
+++ b/chain/bitcoind_events_test.go
@@ -67,6 +67,9 @@ func TestBitcoindEvents(t *testing.T) {
 			// mempool.
 			btcClient = setupBitcoind(t, addr, test.rpcPolling)
 			testNotifySpentMempool(t, miner1, btcClient)
+
+			// Test looking up mempool for input spent.
+			testLookupInputMempoolSpend(t, miner1, btcClient)
 		})
 	}
 }
@@ -214,6 +217,46 @@ func testNotifySpentMempool(t *testing.T, miner *rpctest.Harness,
 	}
 }
 
+// testLookupInputMempoolSpend tests that LookupInputMempoolSpend returns the
+// correct tx hash and whether the input has been spent in the mempool.
+func testLookupInputMempoolSpend(t *testing.T, miner *rpctest.Harness,
+	client *BitcoindClient) {
+
+	rt := require.New(t)
+
+	script, _, err := randPubKeyHashScript()
+	rt.NoError(err)
+
+	// Create a test tx.
+	tx, err := miner.CreateTransaction(
+		[]*wire.TxOut{{Value: 1000, PkScript: script}}, 5, false,
+	)
+	rt.NoError(err)
+
+	// Lookup the input in mempool.
+	op := tx.TxIn[0].PreviousOutPoint
+	txid, found := client.LookupInputMempoolSpend(op)
+
+	// Expect that the input has not been spent in the mempool.
+	rt.False(found)
+	rt.Zero(txid)
+
+	// Send the tx which will put it in the mempool.
+	_, err = client.SendRawTransaction(tx, true)
+	rt.NoError(err)
+
+	// Lookup the input again should return the spending tx.
+	//
+	// NOTE: We need to wait for the tx to propagate to the mempool.
+	rt.Eventually(func() bool {
+		txid, found = client.LookupInputMempoolSpend(op)
+		return found
+	}, 5*time.Second, 100*time.Millisecond)
+
+	// Check the expected txid is returned.
+	rt.Equal(tx.TxHash(), txid)
+}
+
 // testReorg tests that the given BitcoindClient correctly responds to a chain
 // re-org.
 func testReorg(t *testing.T, miner1, miner2 *rpctest.Harness,
diff --git a/chain/bitcoind_zmq_events.go b/chain/bitcoind_zmq_events.go
index 0f8b2f5b91..66e1f3388c 100644
--- a/chain/bitcoind_zmq_events.go
+++ b/chain/bitcoind_zmq_events.go
@@ -2,7 +2,6 @@ package chain
 
 import (
 	"bytes"
-	"encoding/json"
 	"fmt"
 	"io"
 	"math/rand"
@@ -219,22 +218,6 @@ func (b *bitcoindZMQEvents) BlockNotifications() <-chan *wire.MsgBlock {
 	return b.blockNtfns
 }
 
-// getTxSpendingPrevOutReq is the rpc request format for bitcoind's
-// gettxspendingprevout call.
-type getTxSpendingPrevOutReq struct {
-	Txid string `json:"txid"`
-	Vout uint32 `json:"vout"`
-}
-
-// getTxSpendingPrevOutResp is the rpc response format for bitcoind's
-// gettxspendingprevout call. It returns the "spendingtxid" if one exists in
-// the mempool.
-type getTxSpendingPrevOutResp struct {
-	Txid         string  `json:"txid"`
-	Vout         float64 `json:"vout"`
-	SpendingTxid string  `json:"spendingtxid"`
-}
-
 // LookupInputSpend returns the transaction that spends the given outpoint
 // found in the mempool.
 func (b *bitcoindZMQEvents) LookupInputSpend(
@@ -501,28 +484,7 @@ func (b *bitcoindZMQEvents) mempoolPoller() {
 func getTxSpendingPrevOut(op wire.OutPoint,
 	client *rpcclient.Client) (chainhash.Hash, bool) {
 
-	prevoutReq := &getTxSpendingPrevOutReq{
-		Txid: op.Hash.String(), Vout: op.Index,
-	}
-
-	// The RPC takes an array of prevouts so we have an array with a single
-	// item since we don't yet batch calls to LookupInputSpend.
-	prevoutArr := []*getTxSpendingPrevOutReq{prevoutReq}
-
-	req, err := json.Marshal(prevoutArr)
-	if err != nil {
-		return chainhash.Hash{}, false
-	}
-
-	resp, err := client.RawRequest(
-		"gettxspendingprevout", []json.RawMessage{req},
-	)
-	if err != nil {
-		return chainhash.Hash{}, false
-	}
-
-	var prevoutResps []getTxSpendingPrevOutResp
-	err = json.Unmarshal(resp, &prevoutResps)
+	prevoutResps, err := client.GetTxSpendingPrevOut([]wire.OutPoint{op})
 	if err != nil {
 		return chainhash.Hash{}, false
 	}
diff --git a/chain/btcd.go b/chain/btcd.go
index 4ddb183c40..85d8013a9f 100644
--- a/chain/btcd.go
+++ b/chain/btcd.go
@@ -49,6 +49,8 @@ var _ Interface = (*RPCClient)(nil)
 // but must be done using the Start method.  If the remote server does not
 // operate on the same bitcoin network as described by the passed chain
 // parameters, the connection will be disconnected.
+//
+// TODO(yy): deprecate it in favor of NewRPCClientWithConfig.
 func NewRPCClient(chainParams *chaincfg.Params, connect, user, pass string, certs []byte,
 	disableTLS bool, reconnectAttempts int) (*RPCClient, error) {
 
@@ -91,6 +93,109 @@ func NewRPCClient(chainParams *chaincfg.Params, connect, user, pass string, cert
 	return client, nil
 }
 
+// RPCClientConfig defines the config options used when initializing the RPC
+// Client.
+type RPCClientConfig struct {
+	// Conn describes the connection configuration parameters for the
+	// client.
+	Conn *rpcclient.ConnConfig
+
+	// Params defines a Bitcoin network by its parameters.
+	Chain *chaincfg.Params
+
+	// NotificationHandlers defines callback function pointers to invoke
+	// with notifications. If not set, the default handlers defined in this
+	// client will be used.
+	NotificationHandlers *rpcclient.NotificationHandlers
+
+	// ReconnectAttempts defines the number to reties (each after an
+	// increasing backoff) if the connection can not be established.
+	ReconnectAttempts int
+}
+
+// validate checks the required config options are set.
+func (r *RPCClientConfig) validate() error {
+	if r == nil {
+		return errors.New("missing rpc config")
+	}
+
+	// Make sure retry attempts is positive.
+	if r.ReconnectAttempts < 0 {
+		return errors.New("reconnectAttempts must be positive")
+	}
+
+	// Make sure the chain params are configed.
+	if r.Chain == nil {
+		return errors.New("missing chain params config")
+	}
+
+	// Make sure connection config is supplied.
+	if r.Conn == nil {
+		return errors.New("missing conn config")
+	}
+
+	// If disableTLS is false, the remote RPC certificate must be provided
+	// in the certs slice.
+	if !r.Conn.DisableTLS && r.Conn.Certificates == nil {
+		return errors.New("must provide certs when TLS is enabled")
+	}
+
+	return nil
+}
+
+// NewRPCClientWithConfig creates a client connection to the server based on
+// the config options supplised.
+//
+// The connection is not established immediately, but must be done using the
+// Start method.  If the remote server does not operate on the same bitcoin
+// network as described by the passed chain parameters, the connection will be
+// disconnected.
+func NewRPCClientWithConfig(cfg *RPCClientConfig) (*RPCClient, error) {
+	// Make sure the config is valid.
+	if err := cfg.validate(); err != nil {
+		return nil, err
+	}
+
+	// Mimic the old behavior defined in `NewRPCClient`. We will remove
+	// these hard-codings once this package is more properly refactored.
+	cfg.Conn.DisableAutoReconnect = false
+	cfg.Conn.DisableConnectOnNew = true
+
+	client := &RPCClient{
+		connConfig:          cfg.Conn,
+		chainParams:         cfg.Chain,
+		reconnectAttempts:   cfg.ReconnectAttempts,
+		enqueueNotification: make(chan interface{}),
+		dequeueNotification: make(chan interface{}),
+		currentBlock:        make(chan *waddrmgr.BlockStamp),
+		quit:                make(chan struct{}),
+	}
+
+	// Use the configed notification callbacks, if not set, default to the
+	// callbacks defined in this package.
+	ntfnCallbacks := cfg.NotificationHandlers
+	if ntfnCallbacks == nil {
+		ntfnCallbacks = &rpcclient.NotificationHandlers{
+			OnClientConnected:   client.onClientConnect,
+			OnBlockConnected:    client.onBlockConnected,
+			OnBlockDisconnected: client.onBlockDisconnected,
+			OnRecvTx:            client.onRecvTx,
+			OnRedeemingTx:       client.onRedeemingTx,
+			OnRescanFinished:    client.onRescanFinished,
+			OnRescanProgress:    client.onRescanProgress,
+		}
+	}
+
+	// Create the RPC client using the above config.
+	rpcClient, err := rpcclient.New(client.connConfig, ntfnCallbacks)
+	if err != nil {
+		return nil, err
+	}
+
+	client.Client = rpcClient
+	return client, nil
+}
+
 // BackEnd returns the name of the driver.
 func (c *RPCClient) BackEnd() string {
 	return "btcd"
@@ -465,3 +570,11 @@ func (c *RPCClient) POSTClient() (*rpcclient.Client, error) {
 	configCopy.HTTPPostMode = true
 	return rpcclient.New(&configCopy, nil)
 }
+
+// LookupInputMempoolSpend returns the transaction hash and true if the given
+// input is found being spent in mempool, otherwise it returns nil and false.
+func (c *RPCClient) LookupInputMempoolSpend(op wire.OutPoint) (
+	chainhash.Hash, bool) {
+
+	return getTxSpendingPrevOut(op, c.Client)
+}
diff --git a/chain/btcd_test.go b/chain/btcd_test.go
new file mode 100644
index 0000000000..5d0abb575b
--- /dev/null
+++ b/chain/btcd_test.go
@@ -0,0 +1,58 @@
+package chain
+
+import (
+	"testing"
+
+	"github.com/btcsuite/btcd/chaincfg"
+	"github.com/btcsuite/btcd/rpcclient"
+	"github.com/stretchr/testify/require"
+)
+
+// TestValidateConfig checks the `validate` method on the RPCClientConfig
+// behaves as expected.
+func TestValidateConfig(t *testing.T) {
+	t.Parallel()
+
+	rt := require.New(t)
+
+	// ReconnectAttempts must be positive.
+	cfg := &RPCClientConfig{
+		ReconnectAttempts: -1,
+	}
+	rt.ErrorContains(cfg.validate(), "reconnectAttempts")
+
+	// Must specify a chain params.
+	cfg = &RPCClientConfig{
+		ReconnectAttempts: 1,
+	}
+	rt.ErrorContains(cfg.validate(), "chain params")
+
+	// Must specify a connection config.
+	cfg = &RPCClientConfig{
+		ReconnectAttempts: 1,
+		Chain:             &chaincfg.Params{},
+	}
+	rt.ErrorContains(cfg.validate(), "conn config")
+
+	// Must specify a certificate when using TLS.
+	cfg = &RPCClientConfig{
+		ReconnectAttempts: 1,
+		Chain:             &chaincfg.Params{},
+		Conn:              &rpcclient.ConnConfig{},
+	}
+	rt.ErrorContains(cfg.validate(), "certs")
+
+	// Validate config.
+	cfg = &RPCClientConfig{
+		ReconnectAttempts: 1,
+		Chain:             &chaincfg.Params{},
+		Conn: &rpcclient.ConnConfig{
+			DisableTLS: true,
+		},
+	}
+	rt.NoError(cfg.validate())
+
+	// When a nil config is provided, it should return an error.
+	_, err := NewRPCClientWithConfig(nil)
+	rt.ErrorContains(err, "missing rpc config")
+}
diff --git a/go.mod b/go.mod
index 5c2e69c089..a6546069f8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,7 @@
 module github.com/btcsuite/btcwallet
 
 require (
-	github.com/btcsuite/btcd v0.24.1-0.20240116200649-17fdc5219b36
+	github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af
 	github.com/btcsuite/btcd/btcec/v2 v2.2.2
 	github.com/btcsuite/btcd/btcutil v1.1.5
 	github.com/btcsuite/btcd/btcutil/psbt v1.1.8
diff --git a/go.sum b/go.sum
index 6d99908b02..709752211d 100644
--- a/go.sum
+++ b/go.sum
@@ -4,8 +4,8 @@ github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13P
 github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
 github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08=
 github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
-github.com/btcsuite/btcd v0.24.1-0.20240116200649-17fdc5219b36 h1:Us/FoCuHjjn1OfE278h9QTGuuydc0n+SA+NlycvfNsM=
-github.com/btcsuite/btcd v0.24.1-0.20240116200649-17fdc5219b36/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
+github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af h1:F60A3wst4/fy9Yr1Vn8MYmFlfn7DNLxp8o8UTvhqgBE=
+github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
 github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
 github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
 github.com/btcsuite/btcd/btcec/v2 v2.2.2 h1:5uxe5YjoCq+JeOpg0gZSNHuFgeogrocBYxvg6w9sAgc=