Schedule cloud function to update user metrics: totalPnL, creatorVolume

This commit is contained in:
jahooma 2022-01-17 15:39:26 -06:00
parent 673c432bb9
commit 56c7e2597d
5 changed files with 103 additions and 2 deletions

View File

@ -7,4 +7,6 @@ export type User = {
balance: number
createdTime: number
lastUpdatedTime: number
totalPnLCached: number
creatorVolumeCached: number
}

View File

@ -10,3 +10,4 @@ export * from './stripe'
export * from './sell-bet'
export * from './create-contract'
export * from './update-contract-metrics'
export * from './update-user-metrics'

View File

@ -4,6 +4,14 @@ import Stripe from 'stripe'
import { payUser } from './utils'
export type StripeTransaction = {
userId: string
manticDollarQuantity: number
sessionId: string
session: any
timestamp: number
}
const stripe = new Stripe(functions.config().stripe.apikey, {
apiVersion: '2020-08-27',
typescript: true,
@ -111,12 +119,15 @@ const issueMoneys = async (session: any) => {
const { userId, manticDollarQuantity } = session.metadata
const payout = Number.parseInt(manticDollarQuantity)
await firestore.collection('stripe-transactions').add({
const transaction: StripeTransaction = {
userId,
manticDollarQuantity: payout, // save as number
sessionId,
session,
})
timestamp: Date.now(),
}
await firestore.collection('stripe-transactions').add(transaction)
await payUser(userId, payout)

View File

@ -0,0 +1,85 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getValues } from './utils'
import { Contract } from '../../common/contract'
import { Bet } from '../../common/bet'
import { User } from '../../common/user'
import { calculatePayout } from '../../common/calculate'
import { StripeTransaction } from '.'
const firestore = admin.firestore()
export const updateUserMetrics = functions.pubsub
.schedule('every 15 minutes')
.onRun(async () => {
const [users, contracts] = await Promise.all([
getValues<User>(firestore.collection('users')),
getValues<Contract>(firestore.collection('contracts')),
])
const contractsDict = _.fromPairs(
contracts.map((contract) => [contract.id, contract])
)
await Promise.all(
users.map(async (user) => {
const investmentValue = await computeInvestmentValue(
user,
contractsDict
)
const deposits = await getValues<StripeTransaction>(
firestore
.collection('stripe-transactions')
.where('userId', '==', user.id)
)
const totalDeposits =
1000 + _.sumBy(deposits, (deposit) => deposit.manticDollarQuantity)
const totalValue = user.balance + investmentValue
const totalPnL = totalValue - totalDeposits
const creatorVolume = await computeTotalVolume(user, contractsDict)
return firestore.collection('users').doc(user.id).update({
totalPnLCached: totalPnL,
creatorVolumeCached: creatorVolume,
})
})
)
})
const computeInvestmentValue = async (
user: User,
contractsDict: _.Dictionary<Contract>
) => {
const query = firestore.collectionGroup('bets').where('userId', '==', user.id)
const bets = await getValues<Bet>(query)
return _.sumBy(bets, (bet) => {
const contract = contractsDict[bet.contractId]
if (!contract || contract.isResolved) return 0
if (bet.sale || bet.isSold) return 0
return calculatePayout(contract, bet, 'MKT')
})
}
const computeTotalVolume = async (
user: User,
contractsDict: _.Dictionary<Contract>
) => {
const creatorContracts = Object.values(contractsDict).filter(
(contract) => contract.creatorId === user.id
)
const volumes = await Promise.all(creatorContracts.map(computeVolume))
return _.sum(volumes)
}
const computeVolume = async (contract: Contract) => {
const bets = await getValues<Bet>(
firestore.collection(`contracts/${contract.id}/bets`)
)
return _.sumBy(bets, (bet) => Math.abs(bet.amount))
}

View File

@ -74,6 +74,8 @@ export function listenForLogin(onUser: (user: User | null) => void) {
// TODO: use Firestore timestamp?
createdTime: Date.now(),
lastUpdatedTime: Date.now(),
totalPnLCached: 0,
creatorVolumeCached: 0,
}
await setUser(fbUser.uid, user)
}