create multiple choice cloud function
This commit is contained in:
parent
ea80c06e45
commit
3aa22a7772
|
@ -5,12 +5,14 @@ import {
|
|||
CPMMBinaryContract,
|
||||
DPMBinaryContract,
|
||||
FreeResponseContract,
|
||||
MultipleChoiceContract,
|
||||
NumericContract,
|
||||
} from './contract'
|
||||
import { User } from './user'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
import { noFees } from './fees'
|
||||
import { ENV_CONFIG } from './envs/constants'
|
||||
import { Answer } from './answer'
|
||||
|
||||
export const FIXED_ANTE = ENV_CONFIG.fixedAnte ?? 100
|
||||
|
||||
|
@ -111,6 +113,50 @@ export function getFreeAnswerAnte(
|
|||
return anteBet
|
||||
}
|
||||
|
||||
export function getMultipleChoiceAntes(
|
||||
creator: User,
|
||||
contract: MultipleChoiceContract,
|
||||
answers: string[],
|
||||
betDocIds: string[]
|
||||
) {
|
||||
const { totalBets, totalShares } = contract
|
||||
const amount = totalBets['0']
|
||||
const shares = totalShares['0']
|
||||
const p = 1 / answers.length
|
||||
|
||||
const { createdTime } = contract
|
||||
|
||||
const bets: Bet[] = answers.map((answer, i) => ({
|
||||
id: betDocIds[i],
|
||||
userId: creator.id,
|
||||
contractId: contract.id,
|
||||
amount,
|
||||
shares,
|
||||
outcome: i.toString(),
|
||||
probBefore: p,
|
||||
probAfter: p,
|
||||
createdTime,
|
||||
isAnte: true,
|
||||
fees: noFees,
|
||||
}))
|
||||
|
||||
const { username, name, avatarUrl } = creator
|
||||
|
||||
const answerObjects: Answer[] = answers.map((answer, i) => ({
|
||||
id: i.toString(),
|
||||
number: i,
|
||||
contractId: contract.id,
|
||||
createdTime,
|
||||
userId: creator.id,
|
||||
username,
|
||||
name,
|
||||
avatarUrl,
|
||||
text: answer,
|
||||
}))
|
||||
|
||||
return { bets, answerObjects }
|
||||
}
|
||||
|
||||
export function getNumericAnte(
|
||||
anteBettorId: string,
|
||||
contract: NumericContract,
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
CPMM,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
MultipleChoice,
|
||||
Numeric,
|
||||
outcomeType,
|
||||
PseudoNumeric,
|
||||
|
@ -30,7 +31,10 @@ export function getNewContract(
|
|||
bucketCount: number,
|
||||
min: number,
|
||||
max: number,
|
||||
isLogScale: boolean
|
||||
isLogScale: boolean,
|
||||
|
||||
// for multiple choice
|
||||
answers: string[]
|
||||
) {
|
||||
const tags = parseTags(
|
||||
[
|
||||
|
@ -48,6 +52,8 @@ export function getNewContract(
|
|||
? getPseudoNumericCpmmProps(initialProb, ante, min, max, isLogScale)
|
||||
: outcomeType === 'NUMERIC'
|
||||
? getNumericProps(ante, bucketCount, min, max)
|
||||
: outcomeType === 'MULTIPLE_CHOICE'
|
||||
? getMultipleChoiceProps(ante, answers)
|
||||
: getFreeAnswerProps(ante)
|
||||
|
||||
const contract: Contract = removeUndefinedProps({
|
||||
|
@ -151,6 +157,26 @@ const getFreeAnswerProps = (ante: number) => {
|
|||
return system
|
||||
}
|
||||
|
||||
const getMultipleChoiceProps = (ante: number, answers: string[]) => {
|
||||
const numAnswers = answers.length
|
||||
const betAnte = ante / numAnswers
|
||||
const betShares = Math.sqrt(ante ** 2 / numAnswers)
|
||||
|
||||
const defaultValues = (x: any) =>
|
||||
Object.fromEntries(range(0, numAnswers).map((k) => [k, x]))
|
||||
|
||||
const system: DPM & MultipleChoice = {
|
||||
mechanism: 'dpm-2',
|
||||
outcomeType: 'MULTIPLE_CHOICE',
|
||||
pool: defaultValues(betAnte),
|
||||
totalShares: defaultValues(betShares),
|
||||
totalBets: defaultValues(betAnte),
|
||||
answers: [],
|
||||
}
|
||||
|
||||
return system
|
||||
}
|
||||
|
||||
const getNumericProps = (
|
||||
ante: number,
|
||||
bucketCount: number,
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
FreeResponseContract,
|
||||
MAX_QUESTION_LENGTH,
|
||||
MAX_TAG_LENGTH,
|
||||
MultipleChoiceContract,
|
||||
NumericContract,
|
||||
OUTCOME_TYPES,
|
||||
} from '../../common/contract'
|
||||
|
@ -20,15 +21,18 @@ import {
|
|||
FIXED_ANTE,
|
||||
getCpmmInitialLiquidity,
|
||||
getFreeAnswerAnte,
|
||||
getMultipleChoiceAntes,
|
||||
getNumericAnte,
|
||||
} from '../../common/antes'
|
||||
import { getNoneAnswer } from '../../common/answer'
|
||||
import { Answer, getNoneAnswer } from '../../common/answer'
|
||||
import { getNewContract } from '../../common/new-contract'
|
||||
import { NUMERIC_BUCKET_COUNT } from '../../common/numeric-constants'
|
||||
import { User } from '../../common/user'
|
||||
import { Group, MAX_ID_LENGTH } from '../../common/group'
|
||||
import { getPseudoProbability } from '../../common/pseudo-numeric'
|
||||
import { JSONContent } from '@tiptap/core'
|
||||
import { zip } from 'lodash'
|
||||
import { Bet } from 'common/bet'
|
||||
|
||||
const descScehma: z.ZodType<JSONContent> = z.lazy(() =>
|
||||
z.intersection(
|
||||
|
@ -79,11 +83,15 @@ const numericSchema = z.object({
|
|||
isLogScale: z.boolean().optional(),
|
||||
})
|
||||
|
||||
const multipleChoiceSchema = z.object({
|
||||
answers: z.string().trim().min(1).array().min(2),
|
||||
})
|
||||
|
||||
export const createmarket = newEndpoint({}, async (req, auth) => {
|
||||
const { question, description, tags, closeTime, outcomeType, groupId } =
|
||||
validate(bodySchema, req.body)
|
||||
|
||||
let min, max, initialProb, isLogScale
|
||||
let min, max, initialProb, isLogScale, answers
|
||||
|
||||
if (outcomeType === 'PSEUDO_NUMERIC' || outcomeType === 'NUMERIC') {
|
||||
let initialValue
|
||||
|
@ -104,10 +112,15 @@ export const createmarket = newEndpoint({}, async (req, auth) => {
|
|||
)
|
||||
else throw new APIError(400, 'Invalid initial probability.')
|
||||
}
|
||||
|
||||
if (outcomeType === 'BINARY') {
|
||||
;({ initialProb } = validate(binarySchema, req.body))
|
||||
}
|
||||
|
||||
if (outcomeType === 'MULTIPLE_CHOICE') {
|
||||
;({ answers } = validate(multipleChoiceSchema, req.body))
|
||||
}
|
||||
|
||||
const userDoc = await firestore.collection('users').doc(auth.uid).get()
|
||||
if (!userDoc.exists) {
|
||||
throw new APIError(400, 'No user exists with the authenticated user ID.')
|
||||
|
@ -167,7 +180,8 @@ export const createmarket = newEndpoint({}, async (req, auth) => {
|
|||
NUMERIC_BUCKET_COUNT,
|
||||
min ?? 0,
|
||||
max ?? 0,
|
||||
isLogScale ?? false
|
||||
isLogScale ?? false,
|
||||
answers ?? []
|
||||
)
|
||||
|
||||
if (ante) await chargeUser(user.id, ante, true)
|
||||
|
@ -189,6 +203,29 @@ export const createmarket = newEndpoint({}, async (req, auth) => {
|
|||
)
|
||||
|
||||
await liquidityDoc.set(lp)
|
||||
} else if (outcomeType === 'MULTIPLE_CHOICE') {
|
||||
const betCol = firestore.collection(`contracts/${contract.id}/bets`)
|
||||
const betDocs = (answers ?? []).map(() => betCol.doc())
|
||||
|
||||
const answerCol = firestore.collection(`contracts/${contract.id}/answers`)
|
||||
const answerDocs = (answers ?? []).map(() => answerCol.doc())
|
||||
|
||||
const { bets, answerObjects } = getMultipleChoiceAntes(
|
||||
user,
|
||||
contract as MultipleChoiceContract,
|
||||
answers ?? [],
|
||||
betDocs.map((bd) => bd.id)
|
||||
)
|
||||
|
||||
await Promise.all(
|
||||
zip(bets, betDocs).map(([bet, doc]) => doc?.create(bet as Bet))
|
||||
)
|
||||
await Promise.all(
|
||||
zip(answerObjects, answerDocs).map(([answer, doc]) =>
|
||||
doc?.create(answer as Answer)
|
||||
)
|
||||
)
|
||||
await contractRef.update({ answers: answerObjects })
|
||||
} else if (outcomeType === 'FREE_RESPONSE') {
|
||||
const noneAnswerDoc = firestore
|
||||
.collection(`contracts/${contract.id}/answers`)
|
||||
|
|
Loading…
Reference in New Issue
Block a user