From c6a60a667896fd3ef9ebc632f89cf3c232bb6d9c Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Tue, 20 Sep 2022 08:42:09 -0400 Subject: [PATCH] Streak & uniques bonus in transaction --- functions/src/on-create-bet.ts | 183 ++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 83 deletions(-) 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 - 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 - 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' ) }