diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py
index 94c56b9..6d7635b 100644
--- a/basicswap/basicswap.py
+++ b/basicswap/basicswap.py
@@ -92,7 +92,6 @@ from .db import (
     create_db,
     CURRENT_DB_VERSION,
     EventLog,
-    firstOrNone,
     getOrderByStr,
     KnownIdentity,
     MessageLink,
@@ -1316,9 +1315,7 @@ class BasicSwap(BaseApp):
             self.closeDB(cursor)
 
     def updateIdentityBidState(self, cursor, address: str, bid) -> None:
-        identity_stats = firstOrNone(
-            self.query(KnownIdentity, cursor, {"address": address})
-        )
+        identity_stats = self.queryOne(KnownIdentity, cursor, {"address": address})
         if not identity_stats:
             identity_stats = KnownIdentity(
                 active_ind=1, address=address, created_at=self.getTime()
@@ -1357,17 +1354,15 @@ class BasicSwap(BaseApp):
     ) -> Optional[bytes]:
         try:
             use_cursor = self.openDB(cursor)
-            tx = firstOrNone(
-                self.query(
-                    PrefundedTx,
-                    use_cursor,
-                    {
-                        "linked_type": linked_type,
-                        "linked_id": linked_id,
-                        "tx_type": tx_type,
-                        "used_by": None,
-                    },
-                )
+            tx = self.queryOne(
+                PrefundedTx,
+                use_cursor,
+                {
+                    "linked_type": linked_type,
+                    "linked_id": linked_id,
+                    "tx_type": tx_type,
+                    "used_by": None,
+                },
             )
             if tx is None:
                 return None
@@ -1398,7 +1393,7 @@ class BasicSwap(BaseApp):
         ci_to = self.ci(offer.coin_from if reverse_bid else offer.coin_to)
 
         if offer.swap_type == SwapTypes.XMR_SWAP:
-            xmr_swap = firstOrNone(self.query(XmrSwap, cursor, {"bid_id": bid.bid_id}))
+            xmr_swap = self.queryOne(XmrSwap, cursor, {"bid_id": bid.bid_id})
             self.watchXmrSwap(bid, offer, xmr_swap, cursor)
             if (
                 ci_to.watch_blocks_for_scripts()
@@ -1556,9 +1551,11 @@ class BasicSwap(BaseApp):
             return
         self.log.info("Loading data from db")
         self.swaps_in_progress.clear()
+        bid_cursor = None
         try:
             cursor = self.openDB()
-            for bid in self.query(Bid, cursor):
+            bid_cursor = self.getNewDBCursor()
+            for bid in self.query(Bid, bid_cursor):
                 if bid.in_progress == 1 or (
                     bid.state
                     and bid.state > BidStates.BID_RECEIVED
@@ -1571,14 +1568,15 @@ class BasicSwap(BaseApp):
                         try:
                             bid.setState(BidStates.BID_ERROR, "Failed to activate")
 
-                            offer = firstOrNone(
-                                self.query(Offer, cursor, {"offer_id": bid.offer_id})
+                            offer = self.queryOne(
+                                Offer, cursor, {"offer_id": bid.offer_id}
                             )
                             self.deactivateBid(cursor, offer, bid)
                         except Exception as ex:
                             self.logException(f"Further error deactivating: {ex}")
             self.buildNotificationsCache(cursor)
         finally:
+            self.closeDBCursor(bid_cursor)
             self.closeDB(cursor)
 
     def getActiveBidMsgValidTime(self) -> int:
@@ -2172,7 +2170,7 @@ class BasicSwap(BaseApp):
 
         cursor = self.openDB()
         try:
-            offer = firstOrNone(self.query(Offer, cursor, {"offer_id": offer_id}))
+            offer = self.queryOne(Offer, cursor, {"offer_id": offer_id})
 
             if (
                 offer.security_token is not None
@@ -2206,7 +2204,7 @@ class BasicSwap(BaseApp):
         self.log.info("Archiving offer %s", offer_id.hex())
         cursor = self.openDB()
         try:
-            offer = firstOrNone(self.query(Offer, cursor, {"offer_id": offer_id}))
+            offer = self.queryOne(Offer, cursor, {"offer_id": offer_id})
 
             if offer.active_ind != 1:
                 raise ValueError("Offer is not active")
@@ -2226,19 +2224,17 @@ class BasicSwap(BaseApp):
         self.log.info("Editing offer %s", offer_id.hex())
         cursor = self.openDB()
         try:
-            offer = firstOrNone(self.query(Offer, cursor, {"offer_id": offer_id}))
+            offer = self.queryOne(Offer, cursor, {"offer_id": offer_id})
             ensure(offer, f"Offer not found: {offer_id.hex()}.")
             if "automation_strat_id" in data:
                 new_automation_strat_id = data["automation_strat_id"]
-                link = firstOrNone(
-                    self.query(
-                        Offer,
-                        cursor,
-                        {
-                            "linked_type": int(Concepts.OFFER),
-                            "linked_id": offer.offer_id,
-                        },
-                    )
+                link = self.queryOne(
+                    Offer,
+                    cursor,
+                    {
+                        "linked_type": int(Concepts.OFFER),
+                        "linked_id": offer.offer_id,
+                    },
                 )
                 if not link:
                     if new_automation_strat_id > 0:
@@ -2414,12 +2410,10 @@ class BasicSwap(BaseApp):
         try:
             use_cursor = self.openDB(cursor)
 
-            record = firstOrNone(
-                self.query(
-                    PooledAddress,
-                    use_cursor,
-                    {"coin_type": int(coin_type), "bid_id": None},
-                )
+            record = self.queryOne(
+                PooledAddress,
+                use_cursor,
+                {"coin_type": int(coin_type), "bid_id": None},
             )
             if not record:
                 address = self.getReceiveAddressForCoin(coin_type)
@@ -2445,12 +2439,10 @@ class BasicSwap(BaseApp):
         try:
             cursor = self.openDB()
             try:
-                record = firstOrNone(
-                    self.query(
-                        PooledAddress,
-                        cursor,
-                        {"tx_type": int(tx_type), "bid_id": bid_id},
-                    )
+                record = self.queryOne(
+                    PooledAddress,
+                    cursor,
+                    {"tx_type": int(tx_type), "bid_id": bid_id},
                 )
                 self.log.debug("Returning address to pool addr {}".format(record.addr))
 
@@ -3077,7 +3069,7 @@ class BasicSwap(BaseApp):
     def getOffer(self, offer_id: bytes, cursor=None):
         try:
             use_cursor = self.openDB(cursor)
-            return firstOrNone(self.query(Offer, use_cursor, {"offer_id": offer_id}))
+            return self.queryOne(Offer, use_cursor, {"offer_id": offer_id})
         finally:
             if cursor is None:
                 self.closeDB(use_cursor, commit=False)
@@ -3108,10 +3100,10 @@ class BasicSwap(BaseApp):
                 bid.txns[stx.tx_type] = stx
 
     def getXmrBidFromSession(self, cursor, bid_id: bytes):
-        bid = firstOrNone(self.query(Bid, cursor, {"bid_id": bid_id}))
+        bid = self.queryOne(Bid, cursor, {"bid_id": bid_id})
         xmr_swap = None
         if bid:
-            xmr_swap = firstOrNone(self.query(XmrSwap, cursor, {"bid_id": bid_id}))
+            xmr_swap = self.queryOne(XmrSwap, cursor, {"bid_id": bid_id})
             self.loadBidTxns(bid, cursor)
         return bid, xmr_swap
 
@@ -3123,12 +3115,10 @@ class BasicSwap(BaseApp):
             self.closeDB(cursor, commit=False)
 
     def getXmrOfferFromSession(self, cursor, offer_id: bytes):
-        offer = firstOrNone(self.query(Offer, cursor, {"offer_id": offer_id}))
+        offer = self.queryOne(Offer, cursor, {"offer_id": offer_id})
         xmr_offer = None
         if offer:
-            xmr_offer = firstOrNone(
-                self.query(XmrOffer, cursor, {"offer_id": offer_id})
-            )
+            xmr_offer = self.queryOne(XmrOffer, cursor, {"offer_id": offer_id})
         return offer, xmr_offer
 
     def getXmrOffer(self, offer_id: bytes, cursor=None):
@@ -3142,7 +3132,7 @@ class BasicSwap(BaseApp):
     def getBid(self, bid_id: bytes, cursor=None, with_txns=True):
         try:
             use_cursor = self.openDB(cursor)
-            bid = firstOrNone(self.query(Bid, use_cursor, {"bid_id": bid_id}))
+            bid = self.queryOne(Bid, use_cursor, {"bid_id": bid_id})
             if bid and with_txns:
                 self.loadBidTxns(bid, use_cursor)
             return bid
@@ -3153,12 +3143,10 @@ class BasicSwap(BaseApp):
     def getBidAndOffer(self, bid_id: bytes, cursor=None, with_txns=True):
         try:
             use_cursor = self.openDB(cursor)
-            bid = firstOrNone(self.query(Bid, use_cursor, {"bid_id": bid_id}))
+            bid = self.queryOne(Bid, use_cursor, {"bid_id": bid_id})
             offer = None
             if bid:
-                offer = firstOrNone(
-                    self.query(Offer, use_cursor, {"offer_id": bid.offer_id})
-                )
+                offer = self.queryOne(Offer, use_cursor, {"offer_id": bid.offer_id})
                 if with_txns:
                     self.loadBidTxns(bid, use_cursor)
             return bid, offer
@@ -3174,17 +3162,13 @@ class BasicSwap(BaseApp):
             xmr_offer = None
             events = []
 
-            bid = firstOrNone(self.query(Bid, cursor, {"bid_id": bid_id}))
+            bid = self.queryOne(Bid, cursor, {"bid_id": bid_id})
             if bid:
-                offer = firstOrNone(
-                    self.query(Offer, cursor, {"offer_id": bid.offer_id})
-                )
+                offer = self.queryOne(Offer, cursor, {"offer_id": bid.offer_id})
                 if offer and offer.swap_type == SwapTypes.XMR_SWAP:
-                    xmr_swap = firstOrNone(
-                        self.query(XmrSwap, cursor, {"bid_id": bid.bid_id})
-                    )
-                    xmr_offer = firstOrNone(
-                        self.query(XmrOffer, cursor, {"offer_id": bid.offer_id})
+                    xmr_swap = self.queryOne(XmrSwap, cursor, {"bid_id": bid.bid_id})
+                    xmr_offer = self.queryOne(
+                        XmrOffer, cursor, {"offer_id": bid.offer_id}
                     )
                 self.loadBidTxns(bid, cursor)
                 if list_events:
@@ -3197,9 +3181,7 @@ class BasicSwap(BaseApp):
     def getIdentity(self, address: str):
         try:
             cursor = self.openDB()
-            identity = firstOrNone(
-                self.query(KnownIdentity, cursor, {"address": address})
-            )
+            identity = self.queryOne(KnownIdentity, cursor, {"address": address})
             return identity
         finally:
             self.closeDB(cursor, commit=False)
@@ -4998,14 +4980,12 @@ class BasicSwap(BaseApp):
         try:
             cursor = self.openDB()
 
-            xmr_offer = firstOrNone(
-                self.query(XmrOffer, cursor, {"offer_id": offer.offer_id})
-            )
+            xmr_offer = self.queryOne(XmrOffer, cursor, {"offer_id": offer.offer_id})
             ensure(
                 xmr_offer,
                 "Adaptor-sig offer not found: {}.".format(offer.offer_id.hex()),
             )
-            xmr_swap = firstOrNone(self.query(XmrSwap, cursor, {"bid_id": bid.bid_id}))
+            xmr_swap = self.queryOne(XmrSwap, cursor, {"bid_id": bid.bid_id})
             ensure(xmr_swap, "Adaptor-sig swap not found: {}.".format(bid.bid_id.hex()))
 
             if TxTypes.XMR_SWAP_A_LOCK_REFUND in bid.txns:
@@ -6842,10 +6822,14 @@ class BasicSwap(BaseApp):
     def checkXmrSwaps(self) -> None:
         now: int = self.getTime()
         ttl_xmr_split_messages = 60 * 60
+        bid_cursor = None
         try:
             cursor = self.openDB()
-            q = self.query(Bid, cursor, {"state": int(BidStates.BID_RECEIVING)})
-            for bid in q:
+            bid_cursor = self.getNewDBCursor()
+            q_bids = self.query(
+                Bid, bid_cursor, {"state": int(BidStates.BID_RECEIVING)}
+            )
+            for bid in q_bids:
                 q = cursor.execute(
                     "SELECT COUNT(*) FROM xmr_split_data WHERE bid_id = :bid_id AND msg_type = :msg_type",
                     {"bid_id": bid.bid_id, "msg_type": int(XmrSplitMsgTypes.BID)},
@@ -6887,8 +6871,10 @@ class BasicSwap(BaseApp):
                         ],
                     )
 
-            q = self.query(Bid, cursor, {"state": int(BidStates.BID_RECEIVING_ACC)})
-            for bid in q:
+            q_bids = self.query(
+                Bid, bid_cursor, {"state": int(BidStates.BID_RECEIVING_ACC)}
+            )
+            for bid in q_bids:
                 q = cursor.execute(
                     "SELECT COUNT(*) FROM xmr_split_data WHERE bid_id = :bid_id AND msg_type = :msg_type",
                     {
@@ -6941,6 +6927,7 @@ class BasicSwap(BaseApp):
                 {"ttl": ttl_xmr_split_messages, "now": now},
             )
         finally:
+            self.closeDBCursor(bid_cursor)
             self.closeDB(cursor)
 
     def processOffer(self, msg) -> None:
@@ -7235,26 +7222,22 @@ class BasicSwap(BaseApp):
         try:
             use_cursor = self.openDB(cursor)
 
-            link = firstOrNone(
-                self.query(
-                    AutomationLink,
-                    use_cursor,
-                    {
-                        "active_ind": 1,
-                        "linked_type": int(Concepts.OFFER),
-                        "linked_id": offer.offer_id,
-                    },
-                )
+            link = self.queryOne(
+                AutomationLink,
+                use_cursor,
+                {
+                    "active_ind": 1,
+                    "linked_type": int(Concepts.OFFER),
+                    "linked_id": offer.offer_id,
+                },
             )
             if link is None:
                 return False
 
-            strategy = firstOrNone(
-                self.query(
-                    AutomationStrategy,
-                    use_cursor,
-                    {"active_ind": 1, "record_id": link.strategy_id},
-                )
+            strategy = self.queryOne(
+                AutomationStrategy,
+                use_cursor,
+                {"active_ind": 1, "record_id": link.strategy_id},
             )
             opts = json.loads(strategy.data.decode("utf-8"))
 
@@ -7308,8 +7291,8 @@ class BasicSwap(BaseApp):
                     "Already have {} bids to complete".format(num_not_completed)
                 )
 
-            identity_stats = firstOrNone(
-                self.query(KnownIdentity, use_cursor, {"address": bid.bid_addr})
+            identity_stats = self.queryOne(
+                KnownIdentity, use_cursor, {"address": bid.bid_addr}
             )
             self.evaluateKnownIdentityForAutoAccept(strategy, identity_stats)
 
@@ -7583,7 +7566,7 @@ class BasicSwap(BaseApp):
         ensure(offer, "Offer not found: {}.".format(bid.offer_id.hex()))
 
         ensure(xmr_offer, "Adaptor-sig offer not found: {}.".format(bid.offer_id.hex()))
-        xmr_swap = firstOrNone(self.query(XmrSwap, cursor, {"bid_id": bid.bid_id}))
+        xmr_swap = self.queryOne(XmrSwap, cursor, {"bid_id": bid.bid_id})
         ensure(xmr_swap, "Adaptor-sig swap not found: {}.".format(bid.bid_id.hex()))
 
         reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from, offer.coin_to)
@@ -7681,7 +7664,7 @@ class BasicSwap(BaseApp):
         offer, xmr_offer = self.getXmrOffer(bid.offer_id, cursor=cursor)
         ensure(offer, "Offer not found: {}.".format(bid.offer_id.hex()))
         ensure(xmr_offer, "Adaptor-sig offer not found: {}.".format(bid.offer_id.hex()))
-        xmr_swap = firstOrNone(self.query(XmrSwap, cursor, {"bid_id": bid.bid_id}))
+        xmr_swap = self.queryOne(XmrSwap, cursor, {"bid_id": bid.bid_id})
         ensure(xmr_swap, "Adaptor-sig swap not found: {}.".format(bid.bid_id.hex()))
 
         reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from, offer.coin_to)
@@ -10724,9 +10707,7 @@ class BasicSwap(BaseApp):
     def getAutomationStrategy(self, strategy_id: int):
         try:
             cursor = self.openDB()
-            return firstOrNone(
-                self.query(AutomationStrategy, cursor, {"record_id": strategy_id})
-            )
+            return self.queryOne(AutomationStrategy, cursor, {"record_id": strategy_id})
         finally:
             self.closeDB(cursor, commit=False)
 
@@ -10734,8 +10715,8 @@ class BasicSwap(BaseApp):
         self.log.debug(f"updateAutomationStrategy {strategy_id}")
         try:
             cursor = self.openDB()
-            strategy = firstOrNone(
-                self.query(AutomationStrategy, cursor, {"record_id": strategy_id})
+            strategy = self.queryOne(
+                AutomationStrategy, cursor, {"record_id": strategy_id}
             )
             if "data" in data:
                 strategy.data = json.dumps(data["data"]).encode("utf-8")
@@ -11062,7 +11043,7 @@ class BasicSwap(BaseApp):
         try:
             rv = []
             for a in addresses:
-                v = firstOrNone(self.query(KnownIdentity, cursor, {"address": a}))
+                v = self.queryOne(KnownIdentity, cursor, {"address": a})
                 rv.append("" if (not v or not v.label) else v.label)
             return rv
         finally:
diff --git a/basicswap/db.py b/basicswap/db.py
index ac496e1..29b5eb1 100644
--- a/basicswap/db.py
+++ b/basicswap/db.py
@@ -749,6 +749,12 @@ class DBMethods:
         assert self.mxDB.locked()
         self._db_con.rollback()
 
+    def closeDBCursor(self, cursor):
+        assert self.mxDB.locked()
+
+        if cursor:
+            cursor.close()
+
     def closeDB(self, cursor, commit=True):
         assert self.mxDB.locked()
 
@@ -955,6 +961,26 @@ class DBMethods:
                 setattr(obj, colname, value)
             yield obj
 
+    def queryOne(
+        self,
+        table_class,
+        cursor,
+        constraints={},
+        order_by={},
+        query_suffix=None,
+        extra_query_data={},
+    ):
+        return firstOrNone(
+            self.query(
+                table_class,
+                cursor,
+                constraints,
+                order_by,
+                query_suffix,
+                extra_query_data,
+            )
+        )
+
     def updateDB(self, obj, cursor, constraints=[]):
         if cursor is None:
             raise ValueError("Cursor is null")