manifold/functions/src/place-bet.ts

75 lines
2.4 KiB
TypeScript
Raw Normal View History

2021-12-11 00:06:17 +00:00
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { Contract } from './types/contract'
import { User } from './types/user'
import { Bet } from './types/bet'
export const placeBet = functions.https.onCall(async (data: {
amount: number
outcome: string
contractId: string
}, context) => {
const userId = context?.auth?.uid
if (!userId)
return { status: 'error', message: 'Not authorized' }
const { amount, outcome, contractId } = data
if (outcome !== 'YES' && outcome !== 'NO')
return { status: 'error', message: 'Invalid outcome' }
// run as transaction to prevent race conditions
return await firestore.runTransaction(async transaction => {
const userDoc = firestore.doc(`users/${userId}`)
const userSnap = await transaction.get(userDoc)
if (!userSnap.exists) return { status: 'error', message: 'User not found' }
const user = userSnap.data() as User
if (user.balanceUsd < amount)
return { status: 'error', message: 'Insufficient balance' }
const contractDoc = firestore.doc(`contracts/${contractId}`)
const contractSnap = await transaction.get(contractDoc)
if (!contractSnap.exists) return { status: 'error', message: 'Invalid contract' }
const contract = contractSnap.data() as Contract
const newBetDoc = firestore.collection(`contracts/${contractId}/bets`).doc()
const { newBet, newPot, newBalance } = getNewBetInfo(user, outcome, amount, contract, newBetDoc.id)
transaction.create(newBetDoc, newBet)
transaction.update(contractDoc, { pot: newPot })
transaction.update(userDoc, { balanceUsd: newBalance })
return { status: 'success' }
})
})
const firestore = admin.firestore()
const getNewBetInfo = (user: User, outcome: 'YES' | 'NO', amount: number, contract: Contract, newBetId: string) => {
const { YES: yesPot, NO: noPot } = contract.pot
const dpmWeight = outcome === 'YES'
? amount * Math.pow(noPot, 2) / (Math.pow(yesPot, 2) + amount * yesPot)
: amount * Math.pow(yesPot, 2) / (Math.pow(noPot, 2) + amount * noPot)
const newBet: Bet = {
id: newBetId,
userId: user.id,
contractId: contract.id,
amount,
dpmWeight,
outcome,
createdTime: Date.now()
}
const newPot = outcome === 'YES'
? { YES: yesPot + amount, NO: noPot }
: { YES: yesPot, NO: noPot + amount }
const newBalance = user.balanceUsd - amount
return { newBet, newPot, newBalance }
}