diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h
--- a/src/avalanche/peermanager.h
+++ b/src/avalanche/peermanager.h
@@ -9,6 +9,7 @@
 #include <avalanche/proof.h>
 #include <avalanche/proofpool.h>
 #include <avalanche/proofradixtreeadapter.h>
+#include <avalanche/stakecontendercache.h>
 #include <coins.h>
 #include <common/bloom.h>
 #include <consensus/validation.h>
@@ -294,6 +295,8 @@
 
     std::unordered_set<ProofId, SaltedProofIdHasher> manualFlakyProofids;
 
+    StakeContenderCache stakeContenderCache;
+
 public:
     static constexpr size_t MAX_REMOTE_PROOFS{100};
 
@@ -452,6 +455,24 @@
         }
     }
 
+    /** Make some of the contender cache API available */
+    void cleanupStakeContenders(const int requestedMinHeight);
+    void addStakeContender(const ProofRef &proof);
+    int getStakeContenderStatus(const StakeContenderId &contenderId,
+                                BlockHash &prevblockhashout) const;
+    void acceptStakeContender(const StakeContenderId &contenderId);
+    void finalizeStakeContender(const StakeContenderId &contenderId);
+    void rejectStakeContender(const StakeContenderId &contenderId);
+    void promoteStakeContendersToBlock(const CBlockIndex *pindex);
+    bool setStakeContenderWinners(const CBlockIndex *pindex,
+                                  const std::vector<CScript> &payoutScripts);
+    size_t getPollableContenders(
+        const BlockHash &prevblockhash, size_t maxPollable,
+        std::vector<StakeContenderId> &pollableContenders) const;
+    bool getStakeContenderWinners(
+        const BlockHash &prevblockhash,
+        std::vector<std::pair<ProofId, CScript>> &winners) const;
+
     /****************************************************
      * Functions which are public for testing purposes. *
      ****************************************************/
diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp
--- a/src/avalanche/peermanager.cpp
+++ b/src/avalanche/peermanager.cpp
@@ -1390,4 +1390,52 @@
     return true;
 }
 
+void PeerManager::cleanupStakeContenders(const int requestedMinHeight) {
+    stakeContenderCache.cleanup(requestedMinHeight);
+}
+
+void PeerManager::addStakeContender(const ProofRef &proof) {
+    const CBlockIndex *tip = WITH_LOCK(cs_main, return chainman.ActiveTip());
+    stakeContenderCache.add(tip, proof);
+}
+
+int PeerManager::getStakeContenderStatus(const StakeContenderId &contenderId,
+                                         BlockHash &prevblockhashout) const {
+    return stakeContenderCache.getVoteStatus(contenderId, prevblockhashout);
+}
+
+void PeerManager::acceptStakeContender(const StakeContenderId &contenderId) {
+    stakeContenderCache.accept(contenderId);
+}
+
+void PeerManager::finalizeStakeContender(const StakeContenderId &contenderId) {
+    stakeContenderCache.finalize(contenderId);
+}
+
+void PeerManager::rejectStakeContender(const StakeContenderId &contenderId) {
+    stakeContenderCache.reject(contenderId);
+}
+
+void PeerManager::promoteStakeContendersToBlock(const CBlockIndex *pindex) {
+    stakeContenderCache.promoteToBlock(pindex, *this);
+}
+
+bool PeerManager::setStakeContenderWinners(
+    const CBlockIndex *pindex, const std::vector<CScript> &payoutScripts) {
+    return stakeContenderCache.setWinners(pindex, payoutScripts);
+}
+
+size_t PeerManager::getPollableContenders(
+    const BlockHash &prevblockhash, size_t maxPollable,
+    std::vector<StakeContenderId> &pollableContenders) const {
+    return stakeContenderCache.getPollableContenders(prevblockhash, maxPollable,
+                                                     pollableContenders);
+}
+
+bool PeerManager::getStakeContenderWinners(
+    const BlockHash &prevblockhash,
+    std::vector<std::pair<ProofId, CScript>> &winners) const {
+    return stakeContenderCache.getWinners(prevblockhash, winners);
+}
+
 } // namespace avalanche
diff --git a/src/avalanche/processor.h b/src/avalanche/processor.h
--- a/src/avalanche/processor.h
+++ b/src/avalanche/processor.h
@@ -11,7 +11,6 @@
 #include <avalanche/proofcomparator.h>
 #include <avalanche/protocol.h>
 #include <avalanche/stakecontender.h>
-#include <avalanche/stakecontendercache.h>
 #include <avalanche/voterecord.h> // For AVALANCHE_MAX_INFLIGHT_POLL
 #include <blockindex.h>
 #include <blockindexcomparators.h>
@@ -255,9 +254,6 @@
     std::unordered_map<BlockHash, StakingReward, SaltedUint256Hasher>
         stakingRewards GUARDED_BY(cs_stakingRewards);
 
-    mutable Mutex cs_stakeContenderCache;
-    StakeContenderCache stakeContenderCache GUARDED_BY(cs_stakeContenderCache);
-
     Processor(Config avaconfig, interfaces::Chain &chain, CConnman *connmanIn,
               ChainstateManager &chainman, CTxMemPool *mempoolIn,
               CScheduler &scheduler, std::unique_ptr<PeerData> peerDataIn,
@@ -348,16 +344,16 @@
     }
     bool isQuorumEstablished() LOCKS_EXCLUDED(cs_main)
         EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_stakingRewards,
-                                 !cs_stakeContenderCache, !cs_finalizedItems);
+                                 !cs_finalizedItems);
     bool canShareLocalProof();
 
     bool computeStakingReward(const CBlockIndex *pindex)
         EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_stakingRewards,
-                                 !cs_stakeContenderCache, !cs_finalizedItems);
+                                 !cs_finalizedItems);
     bool eraseStakingRewardWinner(const BlockHash &prevBlockHash)
         EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards);
     void cleanupStakingRewards(const int minHeight)
-        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards, !cs_stakeContenderCache);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards, !cs_peerManager);
     bool getStakingRewardWinners(
         const BlockHash &prevBlockHash,
         std::vector<std::pair<ProofId, CScript>> &winners) const
@@ -367,7 +363,7 @@
         EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards);
     bool setStakingRewardWinners(const CBlockIndex *pprev,
                                  const std::vector<CScript> &payouts)
-        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards, !cs_stakeContenderCache);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards, !cs_peerManager);
     bool setStakingRewardWinners(
         const CBlockIndex *pprev,
         const std::vector<std::pair<ProofId, CScript>> &winners)
@@ -391,33 +387,30 @@
 
     /** Track votes on stake contenders */
     void addStakeContender(const ProofRef &proof)
-        EXCLUSIVE_LOCKS_REQUIRED(cs_main, !cs_stakeContenderCache);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_main, !cs_peerManager);
     int getStakeContenderStatus(const StakeContenderId &contenderId) const
-        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakeContenderCache, !cs_stakingRewards);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_stakingRewards);
     void acceptStakeContender(const StakeContenderId &contenderId)
-        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakeContenderCache);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager);
     void finalizeStakeContender(const StakeContenderId &contenderId)
-        EXCLUSIVE_LOCKS_REQUIRED(cs_main, !cs_stakeContenderCache,
-                                 !cs_stakingRewards);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_main, !cs_peerManager, !cs_stakingRewards);
     void rejectStakeContender(const StakeContenderId &contenderId)
-        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakeContenderCache);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager);
 
     /** Promote stake contender cache entries to the latest chain tip */
     void promoteStakeContendersToTip()
-        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakeContenderCache, !cs_stakingRewards,
-                                 !cs_peerManager, !cs_finalizationTip,
-                                 !cs_finalizedItems);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakingRewards, !cs_peerManager,
+                                 !cs_finalizationTip, !cs_finalizedItems);
 
 private:
     void updatedBlockTip()
         EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_finalizedItems,
-                                 !cs_finalizationTip, !cs_stakeContenderCache,
-                                 !cs_stakingRewards);
+                                 !cs_finalizationTip, !cs_stakingRewards);
     void transactionAddedToMempool(const CTransactionRef &tx)
         EXCLUSIVE_LOCKS_REQUIRED(!cs_finalizedItems);
     void runEventLoop()
         EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_stakingRewards,
-                                 !cs_stakeContenderCache, !cs_finalizedItems);
+                                 !cs_finalizedItems);
     void clearTimedoutRequests() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager);
     std::vector<CInv> getInvsForNextPoll(bool forPoll = true)
         EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_finalizedItems);
@@ -434,7 +427,7 @@
     bool setContenderStatusForLocalWinners(
         const CBlockIndex *pindex,
         std::vector<StakeContenderId> &pollableContenders)
-        EXCLUSIVE_LOCKS_REQUIRED(!cs_stakeContenderCache, !cs_stakingRewards);
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_stakingRewards);
 
     /**
      * We don't need many blocks but a low false positive rate.
@@ -469,7 +462,7 @@
         bool operator()(const ProofRef &proof) const
             LOCKS_EXCLUDED(cs_peerManager);
         bool operator()(const StakeContenderId &contenderId) const
-            LOCKS_EXCLUDED(cs_stakeContenderCache, cs_stakingRewards);
+            LOCKS_EXCLUDED(cs_peerManager, cs_stakingRewards);
         bool operator()(const CTransactionRef &tx) const;
     };
     bool isWorthPolling(const AnyVoteItem &item) const
diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp
--- a/src/avalanche/processor.cpp
+++ b/src/avalanche/processor.cpp
@@ -956,8 +956,8 @@
     }
 
     if (m_stakingPreConsensus) {
-        WITH_LOCK(cs_stakeContenderCache,
-                  return stakeContenderCache.cleanup(minHeight));
+        WITH_LOCK(cs_peerManager,
+                  return peerManager->cleanupStakeContenders(minHeight));
     }
 }
 
@@ -1003,8 +1003,8 @@
     }
 
     if (m_stakingPreConsensus) {
-        LOCK(cs_stakeContenderCache);
-        stakeContenderCache.setWinners(pprev, payouts);
+        LOCK(cs_peerManager);
+        peerManager->setStakeContenderWinners(pprev, payouts);
     }
 
     LOCK(cs_stakingRewards);
@@ -1035,21 +1035,18 @@
 }
 
 void Processor::addStakeContender(const ProofRef &proof) {
-    AssertLockHeld(cs_main);
-    const CBlockIndex *activeTip = chainman.ActiveTip();
-    WITH_LOCK(cs_stakeContenderCache,
-              return stakeContenderCache.add(activeTip, proof));
+    WITH_LOCK(cs_peerManager, return peerManager->addStakeContender(proof));
 }
 
 int Processor::getStakeContenderStatus(
     const StakeContenderId &contenderId) const {
-    AssertLockNotHeld(cs_stakeContenderCache);
+    AssertLockNotHeld(cs_peerManager);
     AssertLockNotHeld(cs_stakingRewards);
 
     BlockHash prevblockhash;
-    int status = WITH_LOCK(
-        cs_stakeContenderCache,
-        return stakeContenderCache.getVoteStatus(contenderId, prevblockhash));
+    int status =
+        WITH_LOCK(cs_peerManager, return peerManager->getStakeContenderStatus(
+                                      contenderId, prevblockhash));
 
     if (status != -1) {
         std::vector<std::pair<ProofId, CScript>> winners;
@@ -1065,27 +1062,26 @@
 }
 
 void Processor::acceptStakeContender(const StakeContenderId &contenderId) {
-    LOCK(cs_stakeContenderCache);
-    stakeContenderCache.accept(contenderId);
+    LOCK(cs_peerManager);
+    peerManager->acceptStakeContender(contenderId);
 }
 
 void Processor::finalizeStakeContender(const StakeContenderId &contenderId) {
-    AssertLockHeld(cs_main);
-
     const CBlockIndex *tip;
     std::vector<std::pair<ProofId, CScript>> winners;
     {
-        LOCK(cs_stakeContenderCache);
-        stakeContenderCache.finalize(contenderId);
+        LOCK(cs_peerManager);
+        peerManager->finalizeStakeContender(contenderId);
 
         // Get block hash related to this contender. We should not assume the
         // current chain tip is the block this contender is a winner for.
         BlockHash prevblockhash;
-        stakeContenderCache.getVoteStatus(contenderId, prevblockhash);
+        peerManager->getStakeContenderStatus(contenderId, prevblockhash);
 
-        tip = chainman.m_blockman.LookupBlockIndex(prevblockhash);
+        tip = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(
+                                     prevblockhash));
 
-        stakeContenderCache.getWinners(tip->GetBlockHash(), winners);
+        peerManager->getStakeContenderWinners(tip->GetBlockHash(), winners);
     }
 
     // Set staking rewards to include newly finalized contender
@@ -1095,8 +1091,8 @@
 }
 
 void Processor::rejectStakeContender(const StakeContenderId &contenderId) {
-    LOCK(cs_stakeContenderCache);
-    stakeContenderCache.reject(contenderId);
+    LOCK(cs_peerManager);
+    peerManager->rejectStakeContender(contenderId);
 }
 
 void Processor::promoteStakeContendersToTip() {
@@ -1111,8 +1107,7 @@
 
     {
         LOCK(cs_peerManager);
-        LOCK(cs_stakeContenderCache);
-        stakeContenderCache.promoteToBlock(activeTip, *peerManager);
+        peerManager->promoteStakeContendersToBlock(activeTip);
     }
 
     // If staking rewards have not been computed yet, we will try again when
@@ -1137,21 +1132,21 @@
     }
 
     // Set status for local winners
-    LOCK(cs_stakeContenderCache);
+    LOCK(cs_peerManager);
     for (const auto &winner : winners) {
         const StakeContenderId contenderId(prevblockhash, winner.first);
-        stakeContenderCache.finalize(contenderId);
+        peerManager->finalizeStakeContender(contenderId);
     }
 
     // Treat the highest ranking contender similarly to local winners except
     // that it is not automatically included in the winner set (unless it
     // happens to be selected as a local winner).
-    if (stakeContenderCache.getPollableContenders(
-            prevblockhash, AVALANCHE_CONTENDER_MAX_POLLABLE,
-            pollableContenders) > 0) {
+    if (peerManager->getPollableContenders(prevblockhash,
+                                           AVALANCHE_CONTENDER_MAX_POLLABLE,
+                                           pollableContenders) > 0) {
         // Accept the highest ranking contender. This is a no-op if the highest
         // ranking contender is already the local winner.
-        stakeContenderCache.accept(pollableContenders[0]);
+        peerManager->acceptStakeContender(pollableContenders[0]);
         return true;
     }
 
@@ -1448,7 +1443,7 @@
 
 bool Processor::IsWorthPolling::operator()(
     const StakeContenderId &contenderId) const {
-    AssertLockNotHeld(processor.cs_stakeContenderCache);
+    AssertLockNotHeld(processor.cs_peerManager);
     AssertLockNotHeld(processor.cs_stakingRewards);
 
     // Only worth polling for contenders that we know about
@@ -1493,7 +1488,7 @@
 
 bool Processor::GetLocalAcceptance::operator()(
     const StakeContenderId &contenderId) const {
-    AssertLockNotHeld(processor.cs_stakeContenderCache);
+    AssertLockNotHeld(processor.cs_peerManager);
     AssertLockNotHeld(processor.cs_stakingRewards);
 
     return processor.getStakeContenderStatus(contenderId) == 0;
diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp
--- a/src/avalanche/test/processor_tests.cpp
+++ b/src/avalanche/test/processor_tests.cpp
@@ -409,16 +409,13 @@
         const StakeContenderId contenderId(chaintip->GetBlockHash(),
                                            proof->getId());
 
-        {
-            LOCK(cs_main);
-            fixture->m_processor->addStakeContender(proof);
+        fixture->m_processor->addStakeContender(proof);
 
-            // Many of these tests assume that building a new item means it is
-            // accepted by default. Contenders are different in that they are
-            // only accepted if they are a stake winner. We stick the the
-            // convention for these tests and accept the contender.
-            fixture->m_processor->acceptStakeContender(contenderId);
-        }
+        // Many of these tests assume that building a new item means it is
+        // accepted by default. Contenders are different in that they are
+        // only accepted if they are a stake winner. We stick the the
+        // convention for these tests and accept the contender.
+        fixture->m_processor->acceptStakeContender(contenderId);
 
         BOOST_CHECK(
             fixture->m_processor->getStakeContenderStatus(contenderId) == 0);
@@ -2574,11 +2571,8 @@
 
     // Add stake contenders. Without computing staking rewards, the status is
     // pending.
-    {
-        LOCK(cs_main);
-        m_processor->addStakeContender(proof1);
-        m_processor->addStakeContender(proof2);
-    }
+    m_processor->addStakeContender(proof1);
+    m_processor->addStakeContender(proof2);
     BOOST_CHECK_EQUAL(m_processor->getStakeContenderStatus(contender1_block1),
                       -2);
     BOOST_CHECK_EQUAL(m_processor->getStakeContenderStatus(contender2_block1),
@@ -2731,7 +2725,7 @@
         });
 
         // Add it as a stake contender so it will be promoted
-        WITH_LOCK(cs_main, m_processor->addStakeContender(proof));
+        m_processor->addStakeContender(proof);
 
         proofs.emplace_back(std::move(proof));
     }
@@ -2792,7 +2786,7 @@
                                ->m_blockman.LookupBlockIndex(block.GetHash()));
     auto bestproof = buildRandomProof(active_chainstate,
                                       std::numeric_limits<uint32_t>::max());
-    WITH_LOCK(cs_main, m_processor->addStakeContender(bestproof));
+    m_processor->addStakeContender(bestproof);
     AvalancheTest::updatedBlockTip(*m_processor);
 
     // Compute local stake winners
@@ -2827,10 +2821,7 @@
     ProofId localWinnerProofId = localWinnerProof->getId();
     const StakeContenderId localWinnerContenderId(chaintipHash,
                                                   localWinnerProof->getId());
-    {
-        LOCK(cs_main);
-        m_processor->addStakeContender(localWinnerProof);
-    }
+    m_processor->addStakeContender(localWinnerProof);
 
     // Prepare the proof so that it becomes the local stake winner
     m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
@@ -2865,10 +2856,7 @@
          numContenders < AVALANCHE_CONTENDER_MAX_POLLABLE * 10;
          numContenders++) {
         auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
-        {
-            LOCK(cs_main);
-            m_processor->addStakeContender(proof);
-        }
+        m_processor->addStakeContender(proof);
 
         const StakeContenderId contenderId(chaintipHash, proof->getId());
         double rank = contenderId.ComputeProofRewardRank(MIN_VALID_PROOF_SCORE);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -6956,7 +6956,6 @@
                             break;
                         }
                         case avalanche::VoteStatus::Finalized: {
-                            LOCK(cs_main);
                             m_avalanche->finalizeStakeContender(contenderId);
                             break;
                         }
@@ -9217,7 +9216,7 @@
     saveProofIfStaker(node, proofid, nodeid);
 
     if (isStaker && m_opts.avalanche_staking_preconsensus) {
-        WITH_LOCK(cs_main, m_avalanche->addStakeContender(proof));
+        m_avalanche->addStakeContender(proof);
     }
 
     return true;