diff --git a/functions/src/on-create-bet.ts b/functions/src/on-create-bet.ts
index 09ac4203..b2451c62 100644
--- a/functions/src/on-create-bet.ts
+++ b/functions/src/on-create-bet.ts
@@ -78,8 +78,6 @@ export const onCreateBet = functions
     await notifyFills(bet, contract, eventId, bettor)
     await updateBettingStreak(bettor, bet, contract, eventId)
 
-    await firestore.collection('users').doc(bettor.id).update({ lastBetTime })
-
     await revalidateStaticProps(getContractPath(contract))
   })
 
@@ -89,36 +87,42 @@ const updateBettingStreak = async (
   contract: Contract,
   eventId: string
 ) => {
-  const now = Date.now()
-  const currentDateResetTime = currentDateBettingStreakResetTime()
-  // if now is before reset time, use yesterday's reset time
-  const lastDateResetTime = currentDateResetTime - DAY_MS
-  const betStreakResetTime =
-    now < currentDateResetTime ? lastDateResetTime : currentDateResetTime
-  const lastBetTime = user?.lastBetTime ?? 0
+  const { newBettingStreak } = await firestore.runTransaction(async (trans) => {
+    const userDoc = firestore.collection('users').doc(user.id)
+    const bettor = (await trans.get(userDoc)).data() as User
+    const now = Date.now()
+    const currentDateResetTime = currentDateBettingStreakResetTime()
+    // if now is before reset time, use yesterday's reset time
+    const lastDateResetTime = currentDateResetTime - DAY_MS
+    const betStreakResetTime =
+      now < currentDateResetTime ? lastDateResetTime : currentDateResetTime
+    const lastBetTime = bettor?.lastBetTime ?? 0
 
-  // If they've already bet after the reset time
-  if (lastBetTime > betStreakResetTime) return
+    // If they've already bet after the reset time
+    if (lastBetTime > betStreakResetTime) return { newBettingStreak: undefined }
 
-  const newBettingStreak = (user?.currentBettingStreak ?? 0) + 1
-  // Otherwise, add 1 to their betting streak
-  await firestore.collection('users').doc(user.id).update({
-    currentBettingStreak: newBettingStreak,
+    const newBettingStreak = (bettor?.currentBettingStreak ?? 0) + 1
+    // Otherwise, add 1 to their betting streak
+    await trans.update(userDoc, {
+      currentBettingStreak: newBettingStreak,
+      lastBetTime: bet.createdTime,
+    })
+    return { newBettingStreak }
   })
-
-  // Send them the bonus times their streak
-  const bonusAmount = Math.min(
-    BETTING_STREAK_BONUS_AMOUNT * newBettingStreak,
-    BETTING_STREAK_BONUS_MAX
-  )
-  const fromUserId = isProd()
-    ? HOUSE_LIQUIDITY_PROVIDER_ID
-    : DEV_HOUSE_LIQUIDITY_PROVIDER_ID
-  const bonusTxnDetails = {
-    currentBettingStreak: newBettingStreak,
-  }
-  // TODO: set the id of the txn to the eventId to prevent duplicates
+  if (!newBettingStreak) return
   const result = await firestore.runTransaction(async (trans) => {
+    // Send them the bonus times their streak
+    const bonusAmount = Math.min(
+      BETTING_STREAK_BONUS_AMOUNT * newBettingStreak,
+      BETTING_STREAK_BONUS_MAX
+    )
+    const fromUserId = isProd()
+      ? HOUSE_LIQUIDITY_PROVIDER_ID
+      : DEV_HOUSE_LIQUIDITY_PROVIDER_ID
+    const bonusTxnDetails = {
+      currentBettingStreak: newBettingStreak,
+    }
+
     const bonusTxn: TxnData = {
       fromId: fromUserId,
       fromType: 'BANK',
@@ -130,70 +134,80 @@ const updateBettingStreak = async (
       description: JSON.stringify(bonusTxnDetails),
       data: bonusTxnDetails,
     } as Omit<BettingStreakBonusTxn, 'id' | 'createdTime'>
-    return await runTxn(trans, bonusTxn)
+    const { message, txn, status } = await runTxn(trans, bonusTxn)
+    return { message, txn, status, bonusAmount }
   })
-  if (!result.txn) {
+  if (result.status != 'success') {
     log("betting streak bonus txn couldn't be made")
     log('status:', result.status)
     log('message:', result.message)
     return
   }
-
-  await createBettingStreakBonusNotification(
-    user,
-    result.txn.id,
-    bet,
-    contract,
-    bonusAmount,
-    newBettingStreak,
-    eventId
-  )
+  if (result.txn)
+    await createBettingStreakBonusNotification(
+      user,
+      result.txn.id,
+      bet,
+      contract,
+      result.bonusAmount,
+      newBettingStreak,
+      eventId
+    )
 }
 
 const updateUniqueBettorsAndGiveCreatorBonus = async (
-  contract: Contract,
+  oldContract: Contract,
   eventId: string,
   bettor: User
 ) => {
-  let previousUniqueBettorIds = contract.uniqueBettorIds
+  const { newUniqueBettorIds } = await firestore.runTransaction(
+    async (trans) => {
+      const contractDoc = firestore.collection(`contracts`).doc(oldContract.id)
+      const contract = (await trans.get(contractDoc)).data() as Contract
+      let previousUniqueBettorIds = contract.uniqueBettorIds
 
-  if (!previousUniqueBettorIds) {
-    const contractBets = (
-      await firestore.collection(`contracts/${contract.id}/bets`).get()
-    ).docs.map((doc) => doc.data() as Bet)
+      const betsSnap = await trans.get(
+        firestore.collection(`contracts/${contract.id}/bets`)
+      )
+      if (!previousUniqueBettorIds) {
+        const contractBets = betsSnap.docs.map((doc) => doc.data() as Bet)
 
-    if (contractBets.length === 0) {
-      log(`No bets for contract ${contract.id}`)
-      return
+        if (contractBets.length === 0) {
+          return { newUniqueBettorIds: undefined }
+        }
+
+        previousUniqueBettorIds = uniq(
+          contractBets
+            .filter((bet) => bet.createdTime < BONUS_START_DATE)
+            .map((bet) => bet.userId)
+        )
+      }
+
+      const isNewUniqueBettor = !previousUniqueBettorIds.includes(bettor.id)
+      const newUniqueBettorIds = uniq([...previousUniqueBettorIds, bettor.id])
+
+      // Update contract unique bettors
+      if (!contract.uniqueBettorIds || isNewUniqueBettor) {
+        log(`Got ${previousUniqueBettorIds} unique bettors`)
+        isNewUniqueBettor && log(`And a new unique bettor ${bettor.id}`)
+
+        await trans.update(contractDoc, {
+          uniqueBettorIds: newUniqueBettorIds,
+          uniqueBettorCount: newUniqueBettorIds.length,
+        })
+      }
+
+      // No need to give a bonus for the creator's bet
+      if (!isNewUniqueBettor || bettor.id == contract.creatorId)
+        return { newUniqueBettorIds: undefined }
+
+      return { newUniqueBettorIds }
     }
+  )
+  if (!newUniqueBettorIds) return
 
-    previousUniqueBettorIds = uniq(
-      contractBets
-        .filter((bet) => bet.createdTime < BONUS_START_DATE)
-        .map((bet) => bet.userId)
-    )
-  }
-
-  const isNewUniqueBettor = !previousUniqueBettorIds.includes(bettor.id)
-  const newUniqueBettorIds = uniq([...previousUniqueBettorIds, bettor.id])
-
-  // Update contract unique bettors
-  if (!contract.uniqueBettorIds || isNewUniqueBettor) {
-    log(`Got ${previousUniqueBettorIds} unique bettors`)
-    isNewUniqueBettor && log(`And a new unique bettor ${bettor.id}`)
-
-    await firestore.collection(`contracts`).doc(contract.id).update({
-      uniqueBettorIds: newUniqueBettorIds,
-      uniqueBettorCount: newUniqueBettorIds.length,
-    })
-  }
-
-  // No need to give a bonus for the creator's bet
-  if (!isNewUniqueBettor || bettor.id == contract.creatorId) return
-
-  // Create combined txn for all new unique bettors
   const bonusTxnDetails = {
-    contractId: contract.id,
+    contractId: oldContract.id,
     uniqueNewBettorId: bettor.id,
   }
   const fromUserId = isProd()
@@ -202,12 +216,11 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
   const fromSnap = await firestore.doc(`users/${fromUserId}`).get()
   if (!fromSnap.exists) throw new APIError(400, 'From user not found.')
   const fromUser = fromSnap.data() as User
-  // TODO: set the id of the txn to the eventId to prevent duplicates
   const result = await firestore.runTransaction(async (trans) => {
     const bonusTxn: TxnData = {
       fromId: fromUser.id,
       fromType: 'BANK',
-      toId: contract.creatorId,
+      toId: oldContract.creatorId,
       toType: 'USER',
       amount: UNIQUE_BETTOR_BONUS_AMOUNT,
       token: 'M$',
@@ -215,21 +228,25 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
       description: JSON.stringify(bonusTxnDetails),
       data: bonusTxnDetails,
     } as Omit<UniqueBettorBonusTxn, 'id' | 'createdTime'>
-    return await runTxn(trans, bonusTxn)
+    const { status, message, txn } = await runTxn(trans, bonusTxn)
+    return { status, newUniqueBettorIds, message, txn }
   })
 
   if (result.status != 'success' || !result.txn) {
-    log(`No bonus for user: ${contract.creatorId} - status:`, result.status)
+    log(`No bonus for user: ${oldContract.creatorId} - status:`, result.status)
     log('message:', result.message)
   } else {
-    log(`Bonus txn for user: ${contract.creatorId} completed:`, result.txn?.id)
+    log(
+      `Bonus txn for user: ${oldContract.creatorId} completed:`,
+      result.txn?.id
+    )
     await createUniqueBettorBonusNotification(
-      contract.creatorId,
+      oldContract.creatorId,
       bettor,
       result.txn.id,
-      contract,
+      oldContract,
       result.txn.amount,
-      newUniqueBettorIds,
+      result.newUniqueBettorIds,
       eventId + '-unique-bettor-bonus'
     )
   }