This commit is contained in:
Pico2x 2022-10-06 17:04:32 +01:00
commit edbd0feb37

View File

@ -15,7 +15,7 @@ import {
import { slugify } from '../../common/util/slugify' import { slugify } from '../../common/util/slugify'
import { randomString } from '../../common/util/random' import { randomString } from '../../common/util/random'
import { isProd } from './utils' import { chargeUser, getContract, isProd } from './utils'
import { APIError, AuthedUser, newEndpoint, validate, zTimestamp } from './api' import { APIError, AuthedUser, newEndpoint, validate, zTimestamp } from './api'
import { FIXED_ANTE, FREE_MARKETS_PER_USER_MAX } from '../../common/economy' import { FIXED_ANTE, FREE_MARKETS_PER_USER_MAX } from '../../common/economy'
@ -36,7 +36,7 @@ import { getPseudoProbability } from '../../common/pseudo-numeric'
import { JSONContent } from '@tiptap/core' import { JSONContent } from '@tiptap/core'
import { uniq, zip } from 'lodash' import { uniq, zip } from 'lodash'
import { Bet } from '../../common/bet' import { Bet } from '../../common/bet'
import { FieldValue, Transaction } from 'firebase-admin/firestore' import { FieldValue } from 'firebase-admin/firestore'
const descScehma: z.ZodType<JSONContent> = z.lazy(() => const descScehma: z.ZodType<JSONContent> = z.lazy(() =>
z.intersection( z.intersection(
@ -107,7 +107,6 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
visibility = 'public', visibility = 'public',
} = validate(bodySchema, body) } = validate(bodySchema, body)
return await firestore.runTransaction(async (trans) => {
let min, max, initialProb, isLogScale, answers let min, max, initialProb, isLogScale, answers
if (outcomeType === 'PSEUDO_NUMERIC' || outcomeType === 'NUMERIC') { if (outcomeType === 'PSEUDO_NUMERIC' || outcomeType === 'NUMERIC') {
@ -116,8 +115,7 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
if (max - min <= 0.01 || initialValue <= min || initialValue >= max) if (max - min <= 0.01 || initialValue <= min || initialValue >= max)
throw new APIError(400, 'Invalid range.') throw new APIError(400, 'Invalid range.')
initialProb = initialProb = getPseudoProbability(initialValue, min, max, isLogScale) * 100
getPseudoProbability(initialValue, min, max, isLogScale) * 100
if (initialProb < 1 || initialProb > 99) if (initialProb < 1 || initialProb > 99)
if (outcomeType === 'PSEUDO_NUMERIC') if (outcomeType === 'PSEUDO_NUMERIC')
@ -136,7 +134,7 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
;({ answers } = validate(multipleChoiceSchema, body)) ;({ answers } = validate(multipleChoiceSchema, body))
} }
const userDoc = await trans.get(firestore.collection('users').doc(auth.uid)) const userDoc = await firestore.collection('users').doc(auth.uid).get()
if (!userDoc.exists) { if (!userDoc.exists) {
throw new APIError(400, 'No user exists with the authenticated user ID.') throw new APIError(400, 'No user exists with the authenticated user ID.')
} }
@ -152,15 +150,15 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
let group: Group | null = null let group: Group | null = null
if (groupId) { if (groupId) {
const groupDocRef = firestore.collection('groups').doc(groupId) const groupDocRef = firestore.collection('groups').doc(groupId)
const groupDoc = await trans.get(groupDocRef) const groupDoc = await groupDocRef.get()
if (!groupDoc.exists) { if (!groupDoc.exists) {
throw new APIError(400, 'No group exists with the given group ID.') throw new APIError(400, 'No group exists with the given group ID.')
} }
group = groupDoc.data() as Group group = groupDoc.data() as Group
const groupMembersSnap = await trans.get( const groupMembersSnap = await firestore
firestore.collection(`groups/${groupId}/groupMembers`) .collection(`groups/${groupId}/groupMembers`)
) .get()
const groupMemberDocs = groupMembersSnap.docs.map( const groupMemberDocs = groupMembersSnap.docs.map(
(doc) => doc.data() as { userId: string; createdTime: number } (doc) => doc.data() as { userId: string; createdTime: number }
) )
@ -175,7 +173,7 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
) )
} }
} }
const slug = await getSlug(trans, question) const slug = await getSlug(question)
const contractRef = firestore.collection('contracts').doc() const contractRef = firestore.collection('contracts').doc()
console.log( console.log(
@ -226,36 +224,28 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
: DEV_HOUSE_LIQUIDITY_PROVIDER_ID : DEV_HOUSE_LIQUIDITY_PROVIDER_ID
: user.id : user.id
if (ante) { if (ante) await chargeUser(providerId, ante, true)
const delta = FieldValue.increment(-ante) if (deservesFreeMarket)
const providerDoc = firestore.collection('users').doc(providerId) await firestore
await trans.update(providerDoc, { balance: delta, totalDeposits: delta }) .collection('users')
} .doc(user.id)
.update({ freeMarketsCreated: FieldValue.increment(1) })
if (deservesFreeMarket) {
await trans.update(firestore.collection('users').doc(user.id), {
freeMarketsCreated: FieldValue.increment(1),
})
}
await contractRef.create(contract) await contractRef.create(contract)
if (group != null) { if (group != null) {
const groupContractsSnap = await trans.get( const groupContractsSnap = await firestore
firestore.collection(`groups/${groupId}/groupContracts`) .collection(`groups/${groupId}/groupContracts`)
) .get()
const groupContracts = groupContractsSnap.docs.map( const groupContracts = groupContractsSnap.docs.map(
(doc) => doc.data() as { contractId: string; createdTime: number } (doc) => doc.data() as { contractId: string; createdTime: number }
) )
if (!groupContracts.map((c) => c.contractId).includes(contractRef.id)) { if (!groupContracts.map((c) => c.contractId).includes(contractRef.id)) {
await createGroupLinks(trans, group, [contractRef.id], auth.uid) await createGroupLinks(group, [contractRef.id], auth.uid)
const groupContractRef = firestore const groupContractRef = firestore
.collection(`groups/${groupId}/groupContracts`) .collection(`groups/${groupId}/groupContracts`)
.doc(contract.id) .doc(contract.id)
await groupContractRef.set({
await trans.set(groupContractRef, {
contractId: contract.id, contractId: contract.id,
createdTime: Date.now(), createdTime: Date.now(),
}) })
@ -274,7 +264,7 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
ante ante
) )
await trans.set(liquidityDoc, lp) await liquidityDoc.set(lp)
} else if (outcomeType === 'MULTIPLE_CHOICE') { } else if (outcomeType === 'MULTIPLE_CHOICE') {
const betCol = firestore.collection(`contracts/${contract.id}/bets`) const betCol = firestore.collection(`contracts/${contract.id}/bets`)
const betDocs = (answers ?? []).map(() => betCol.doc()) const betDocs = (answers ?? []).map(() => betCol.doc())
@ -292,23 +282,21 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
) )
await Promise.all( await Promise.all(
zip(bets, betDocs).map(([bet, doc]) => zip(bets, betDocs).map(([bet, doc]) => doc?.create(bet as Bet))
doc ? trans.create(doc, bet as Bet) : undefined
)
) )
await Promise.all( await Promise.all(
zip(answerObjects, answerDocs).map(([answer, doc]) => zip(answerObjects, answerDocs).map(([answer, doc]) =>
doc ? trans.create(doc, answer as Answer) : undefined doc?.create(answer as Answer)
) )
) )
await trans.update(contractRef, { answers: answerObjects }) await contractRef.update({ answers: answerObjects })
} else if (outcomeType === 'FREE_RESPONSE') { } else if (outcomeType === 'FREE_RESPONSE') {
const noneAnswerDoc = firestore const noneAnswerDoc = firestore
.collection(`contracts/${contract.id}/answers`) .collection(`contracts/${contract.id}/answers`)
.doc('0') .doc('0')
const noneAnswer = getNoneAnswer(contract.id, user) const noneAnswer = getNoneAnswer(contract.id, user)
await trans.set(noneAnswerDoc, noneAnswer) await noneAnswerDoc.set(noneAnswer)
const anteBetDoc = firestore const anteBetDoc = firestore
.collection(`contracts/${contract.id}/bets`) .collection(`contracts/${contract.id}/bets`)
@ -319,7 +307,7 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
contract as FreeResponseContract, contract as FreeResponseContract,
anteBetDoc.id anteBetDoc.id
) )
await trans.set(anteBetDoc, anteBet) await anteBetDoc.set(anteBet)
} else if (outcomeType === 'NUMERIC') { } else if (outcomeType === 'NUMERIC') {
const anteBetDoc = firestore const anteBetDoc = firestore
.collection(`contracts/${contract.id}/bets`) .collection(`contracts/${contract.id}/bets`)
@ -332,17 +320,16 @@ export async function createMarketHelper(body: any, auth: AuthedUser) {
anteBetDoc.id anteBetDoc.id
) )
await trans.set(anteBetDoc, anteBet) await anteBetDoc.set(anteBet)
} }
return contract return contract
})
} }
const getSlug = async (trans: Transaction, question: string) => { const getSlug = async (question: string) => {
const proposedSlug = slugify(question) const proposedSlug = slugify(question)
const preexistingContract = await getContractFromSlug(trans, proposedSlug) const preexistingContract = await getContractFromSlug(proposedSlug)
return preexistingContract return preexistingContract
? proposedSlug + '-' + randomString() ? proposedSlug + '-' + randomString()
@ -351,31 +338,35 @@ const getSlug = async (trans: Transaction, question: string) => {
const firestore = admin.firestore() const firestore = admin.firestore()
async function getContractFromSlug(trans: Transaction, slug: string) { export async function getContractFromSlug(slug: string) {
const snap = await trans.get( const snap = await firestore
firestore.collection('contracts').where('slug', '==', slug) .collection('contracts')
) .where('slug', '==', slug)
.get()
return snap.empty ? undefined : (snap.docs[0].data() as Contract) return snap.empty ? undefined : (snap.docs[0].data() as Contract)
} }
async function createGroupLinks( async function createGroupLinks(
trans: Transaction,
group: Group, group: Group,
contractIds: string[], contractIds: string[],
userId: string userId: string
) { ) {
for (const contractId of contractIds) { for (const contractId of contractIds) {
const contractRef = firestore.collection('contracts').doc(contractId) const contract = await getContract(contractId)
const contract = (await trans.get(contractRef)).data() as Contract
if (!contract?.groupSlugs?.includes(group.slug)) { if (!contract?.groupSlugs?.includes(group.slug)) {
await trans.update(contractRef, { await firestore
.collection('contracts')
.doc(contractId)
.update({
groupSlugs: uniq([group.slug, ...(contract?.groupSlugs ?? [])]), groupSlugs: uniq([group.slug, ...(contract?.groupSlugs ?? [])]),
}) })
} }
if (!contract?.groupLinks?.map((gl) => gl.groupId).includes(group.id)) { if (!contract?.groupLinks?.map((gl) => gl.groupId).includes(group.id)) {
await trans.update(contractRef, { await firestore
.collection('contracts')
.doc(contractId)
.update({
groupLinks: [ groupLinks: [
{ {
groupId: group.id, groupId: group.id,