diff --git a/functions/src/on-update-contract.ts b/functions/src/on-update-contract.ts index 097ac53a..d667f0d2 100644 --- a/functions/src/on-update-contract.ts +++ b/functions/src/on-update-contract.ts @@ -11,98 +11,108 @@ export const onUpdateContract = functions.firestore .document('contracts/{contractId}') .onUpdate(async (change, context) => { const contract = change.after.data() as Contract + const previousContract = change.before.data() as Contract const { eventId } = context + const { openCommentBounties, closeTime, question } = contract - const contractUpdater = await getUser(contract.creatorId) - if (!contractUpdater) throw new Error('Could not find contract updater') - - const previousValue = change.before.data() as Contract - - // Refund extra unused comment bounties - if (!previousValue.isResolved && contract.isResolved) { - const bountiesLeft = contract.openCommentBounties ?? 0 - if (bountiesLeft <= 0) return - - const outstandingCommentBounties = await getValues( - firestore.collection('txns').where('category', '==', 'COMMENT_BOUNTY') - ) - - const commentBountiesOnThisContract = sortBy( - outstandingCommentBounties.filter( - (bounty) => bounty.data?.contractId === contract.id - ), - (bounty) => bounty.createdTime - ) - - const [toBank, fromBank] = partition( - commentBountiesOnThisContract, - (bounty) => bounty.toType === 'BANK' - ) - if (toBank.length <= fromBank.length) return - - await firestore - .collection('contracts') - .doc(contract.id) - .update({ openCommentBounties: 0 }) - - const refunds = toBank.slice(fromBank.length) - await Promise.all( - refunds.map(async (extraBountyTxn) => { - const result = await firestore.runTransaction(async (trans) => { - const bonusTxn: TxnData = { - fromId: extraBountyTxn.toId, - fromType: 'BANK', - toId: extraBountyTxn.fromId, - toType: 'USER', - amount: extraBountyTxn.amount, - token: 'M$', - category: 'REFUND_COMMENT_BOUNTY', - data: { - contractId: contract.id, - }, - } - return await runTxn(trans, bonusTxn) - }) - - if (result.status != 'success' || !result.txn) { - log( - `Couldn't refund bonus for user: ${extraBountyTxn.fromId} - status:`, - result.status - ) - log('message:', result.message) - } else { - log( - `Refund bonus txn for user: ${extraBountyTxn.fromId} completed:`, - result.txn?.id - ) - } - }) - ) + if ( + !previousContract.isResolved && + contract.isResolved && + (openCommentBounties ?? 0) > 0 + ) { + await handleUnusedCommentBountyRefunds(contract) + // No need to notify users of resolution, that's handled in resolve-market return } if ( - previousValue.closeTime !== contract.closeTime || - previousValue.question !== contract.question + previousContract.closeTime !== closeTime || + previousContract.question !== question ) { - let sourceText = '' - if ( - previousValue.closeTime !== contract.closeTime && - contract.closeTime - ) { - sourceText = contract.closeTime.toString() - } else if (previousValue.question !== contract.question) { - sourceText = contract.question - } - - await createCommentOrAnswerOrUpdatedContractNotification( - contract.id, - 'contract', - 'updated', - contractUpdater, - eventId, - sourceText, - contract - ) + await handleUpdatedCloseTime(previousContract, contract, eventId) } }) + +async function handleUpdatedCloseTime( + previousContract: Contract, + contract: Contract, + eventId: string +) { + const contractUpdater = await getUser(contract.creatorId) + if (!contractUpdater) throw new Error('Could not find contract updater') + let sourceText = '' + if (previousContract.closeTime !== contract.closeTime && contract.closeTime) { + sourceText = contract.closeTime.toString() + } else if (previousContract.question !== contract.question) { + sourceText = contract.question + } + + await createCommentOrAnswerOrUpdatedContractNotification( + contract.id, + 'contract', + 'updated', + contractUpdater, + eventId, + sourceText, + contract + ) +} + +async function handleUnusedCommentBountyRefunds(contract: Contract) { + const outstandingCommentBounties = await getValues( + firestore.collection('txns').where('category', '==', 'COMMENT_BOUNTY') + ) + + const commentBountiesOnThisContract = sortBy( + outstandingCommentBounties.filter( + (bounty) => bounty.data?.contractId === contract.id + ), + (bounty) => bounty.createdTime + ) + + const [toBank, fromBank] = partition( + commentBountiesOnThisContract, + (bounty) => bounty.toType === 'BANK' + ) + if (toBank.length <= fromBank.length) return + + await firestore + .collection('contracts') + .doc(contract.id) + .update({ openCommentBounties: 0 }) + + const refunds = toBank.slice(fromBank.length) + await Promise.all( + refunds.map(async (extraBountyTxn) => { + const result = await firestore.runTransaction(async (trans) => { + const bonusTxn: TxnData = { + fromId: extraBountyTxn.toId, + fromType: 'BANK', + toId: extraBountyTxn.fromId, + toType: 'USER', + amount: extraBountyTxn.amount, + token: 'M$', + category: 'REFUND_COMMENT_BOUNTY', + data: { + contractId: contract.id, + }, + } + return await runTxn(trans, bonusTxn) + }) + + if (result.status != 'success' || !result.txn) { + log( + `Couldn't refund bonus for user: ${extraBountyTxn.fromId} - status:`, + result.status + ) + log('message:', result.message) + } else { + log( + `Refund bonus txn for user: ${extraBountyTxn.fromId} completed:`, + result.txn?.id + ) + } + }) + ) +} + const firestore = admin.firestore()