diff --git a/common/txn.ts b/common/txn.ts index 25edc530..c404059d 100644 --- a/common/txn.ts +++ b/common/txn.ts @@ -8,6 +8,7 @@ type AnyTxnType = | UniqueBettorBonus | BettingStreakBonus | CancelUniqueBettorBonus + | CommentBountyRefund type SourceType = 'USER' | 'CONTRACT' | 'CHARITY' | 'BANK' export type Txn = { @@ -32,6 +33,7 @@ export type Txn = { | 'BETTING_STREAK_BONUS' | 'CANCEL_UNIQUE_BETTOR_BONUS' | 'COMMENT_BOUNTY' + | 'REFUND_COMMENT_BOUNTY' // Any extra data data?: { [key: string]: any } @@ -118,6 +120,15 @@ type CommentBountyWithdrawal = { } } +type CommentBountyRefund = { + fromType: 'BANK' + toType: 'USER' + category: 'REFUND_COMMENT_BOUNTY' + data: { + contractId: string + } +} + export type DonationTxn = Txn & Donation export type TipTxn = Txn & Tip export type ManalinkTxn = Txn & Manalink diff --git a/functions/src/on-update-contract.ts b/functions/src/on-update-contract.ts index 5e2a94c0..181fa534 100644 --- a/functions/src/on-update-contract.ts +++ b/functions/src/on-update-contract.ts @@ -1,7 +1,11 @@ import * as functions from 'firebase-functions' -import { getUser } from './utils' +import { getUser, getValues, log } from './utils' import { createCommentOrAnswerOrUpdatedContractNotification } from './create-notification' import { Contract } from '../../common/contract' +import { Txn } from '../../common/txn' +import { partition, sortBy } from 'lodash' +import { runTxn, TxnData } from './transact' +import * as admin from 'firebase-admin' export const onUpdateContract = functions.firestore .document('contracts/{contractId}') @@ -14,9 +18,63 @@ export const onUpdateContract = functions.firestore const previousValue = change.before.data() as Contract - // Resolution is handled in resolve-market.ts - if (!previousValue.isResolved && contract.isResolved) return + // 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 + + 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 + ) + } + }) + ) + return + } if ( previousValue.closeTime !== contract.closeTime || previousValue.question !== contract.question @@ -42,3 +100,4 @@ export const onUpdateContract = functions.firestore ) } }) +const firestore = admin.firestore() diff --git a/web/components/contract/liquidity-bounty-panel.tsx b/web/components/contract/liquidity-bounty-panel.tsx index e5770f60..4cc7fd70 100644 --- a/web/components/contract/liquidity-bounty-panel.tsx +++ b/web/components/contract/liquidity-bounty-panel.tsx @@ -36,8 +36,6 @@ export function LiquidityBountyPanel(props: { contract: Contract }) { const isCreator = user?.id === contract.creatorId const isAdmin = useAdmin() - if (!showWithdrawal && !isAdmin && !isCreator) return <> - return ( , - } + (isCreator || isAdmin) && + isCPMM && { + title: 'Pool', + content: , + } )} /> )