Address review comments
This commit is contained in:
parent
17401dd74a
commit
c1d7d55f6f
|
@ -2,7 +2,7 @@ import * as admin from 'firebase-admin'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { difference, uniq, mapValues, groupBy, sumBy } from 'lodash'
|
import { difference, uniq, mapValues, groupBy, sumBy } from 'lodash'
|
||||||
|
|
||||||
import { Contract, RESOLUTIONS } from '../../common/contract'
|
import { Contract, resolution, RESOLUTIONS } from '../../common/contract'
|
||||||
import { User } from '../../common/user'
|
import { User } from '../../common/user'
|
||||||
import { Bet } from '../../common/bet'
|
import { Bet } from '../../common/bet'
|
||||||
import { getUser, isProd, payUser } from './utils'
|
import { getUser, isProd, payUser } from './utils'
|
||||||
|
@ -18,18 +18,26 @@ import { LiquidityProvision } from '../../common/liquidity-provision'
|
||||||
import { APIError, newEndpoint, validate } from './api'
|
import { APIError, newEndpoint, validate } from './api'
|
||||||
|
|
||||||
const bodySchema = z.object({
|
const bodySchema = z.object({
|
||||||
outcome: z.enum(RESOLUTIONS),
|
|
||||||
contractId: z.string(),
|
contractId: z.string(),
|
||||||
value: z.number().optional(),
|
})
|
||||||
|
|
||||||
|
const binarySchema = z.object({
|
||||||
|
outcome: z.enum(RESOLUTIONS),
|
||||||
probabilityInt: z.number().gte(0).lt(100).optional(),
|
probabilityInt: z.number().gte(0).lt(100).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const freeResponseSchema = z.object({
|
||||||
|
outcome: z.union([z.enum(['MKT', 'CANCEL']), z.string()]),
|
||||||
resolutions: z.map(z.string(), z.number()).optional(),
|
resolutions: z.map(z.string(), z.number()).optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const numericSchema = z.object({
|
||||||
|
outcome: z.union([z.enum(['CANCEL']), z.string()]),
|
||||||
|
value: z.number().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export const resolvemarket = newEndpoint(['POST'], async (req, auth) => {
|
export const resolvemarket = newEndpoint(['POST'], async (req, auth) => {
|
||||||
const { outcome, value, contractId, probabilityInt, resolutions } = validate(
|
const { contractId } = validate(bodySchema, req.body)
|
||||||
bodySchema,
|
|
||||||
req.body
|
|
||||||
)
|
|
||||||
const userId = auth.uid
|
const userId = auth.uid
|
||||||
if (!userId) throw new APIError(403, 'Not authorized')
|
if (!userId) throw new APIError(403, 'Not authorized')
|
||||||
|
|
||||||
|
@ -40,27 +48,10 @@ export const resolvemarket = newEndpoint(['POST'], async (req, auth) => {
|
||||||
const contract = contractSnap.data() as Contract
|
const contract = contractSnap.data() as Contract
|
||||||
const { creatorId, outcomeType, closeTime } = contract
|
const { creatorId, outcomeType, closeTime } = contract
|
||||||
|
|
||||||
if (outcomeType === 'FREE_RESPONSE') {
|
const { value, resolutions, probabilityInt, outcome } = getResolutionParams(
|
||||||
if (
|
outcomeType,
|
||||||
isNaN(+outcome) &&
|
req.body
|
||||||
!(outcome === 'MKT' && resolutions) &&
|
|
||||||
outcome !== 'CANCEL'
|
|
||||||
)
|
|
||||||
throw new APIError(400, 'Invalid free response outcome')
|
|
||||||
} else if (outcomeType === 'NUMERIC') {
|
|
||||||
if (isNaN(+outcome) && outcome !== 'CANCEL')
|
|
||||||
throw new APIError(400, 'Invalid numeric outcome')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value !== undefined && !isFinite(value))
|
|
||||||
throw new APIError(400, 'Invalid value')
|
|
||||||
|
|
||||||
if (
|
|
||||||
outcomeType === 'BINARY' &&
|
|
||||||
probabilityInt !== undefined &&
|
|
||||||
(probabilityInt < 0 || probabilityInt > 100 || !isFinite(probabilityInt))
|
|
||||||
)
|
)
|
||||||
throw new APIError(400, 'Invalid probability')
|
|
||||||
|
|
||||||
if (creatorId !== userId)
|
if (creatorId !== userId)
|
||||||
throw new APIError(403, 'User is not creator of contract')
|
throw new APIError(403, 'User is not creator of contract')
|
||||||
|
@ -68,7 +59,7 @@ export const resolvemarket = newEndpoint(['POST'], async (req, auth) => {
|
||||||
if (contract.resolution) throw new APIError(400, 'Contract already resolved')
|
if (contract.resolution) throw new APIError(400, 'Contract already resolved')
|
||||||
|
|
||||||
const creator = await getUser(creatorId)
|
const creator = await getUser(creatorId)
|
||||||
if (!creator) throw new APIError(400, 'Creator not found')
|
if (!creator) throw new APIError(500, 'Creator not found')
|
||||||
|
|
||||||
const resolutionProbability =
|
const resolutionProbability =
|
||||||
probabilityInt !== undefined ? probabilityInt / 100 : undefined
|
probabilityInt !== undefined ? probabilityInt / 100 : undefined
|
||||||
|
@ -102,8 +93,9 @@ export const resolvemarket = newEndpoint(['POST'], async (req, auth) => {
|
||||||
resolutionProbability
|
resolutionProbability
|
||||||
)
|
)
|
||||||
|
|
||||||
await contractDoc.update(
|
const updatedContract = {
|
||||||
removeUndefinedProps({
|
...contract,
|
||||||
|
...removeUndefinedProps({
|
||||||
isResolved: true,
|
isResolved: true,
|
||||||
resolution: outcome,
|
resolution: outcome,
|
||||||
resolutionValue: value,
|
resolutionValue: value,
|
||||||
|
@ -112,8 +104,10 @@ export const resolvemarket = newEndpoint(['POST'], async (req, auth) => {
|
||||||
resolutionProbability,
|
resolutionProbability,
|
||||||
resolutions,
|
resolutions,
|
||||||
collectedFees,
|
collectedFees,
|
||||||
})
|
}),
|
||||||
)
|
}
|
||||||
|
|
||||||
|
await contractDoc.update(updatedContract)
|
||||||
|
|
||||||
console.log('contract ', contractId, 'resolved to:', outcome)
|
console.log('contract ', contractId, 'resolved to:', outcome)
|
||||||
|
|
||||||
|
@ -149,10 +143,7 @@ export const resolvemarket = newEndpoint(['POST'], async (req, auth) => {
|
||||||
Object.fromEntries(resolutions || [])
|
Object.fromEntries(resolutions || [])
|
||||||
)
|
)
|
||||||
|
|
||||||
const updatedContractSnap = await contractDoc.get()
|
return updatedContract
|
||||||
if (!updatedContractSnap)
|
|
||||||
throw new APIError(500, 'Updated contract does not exist')
|
|
||||||
return updatedContractSnap.data() as Contract
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const processPayouts = async (payouts: Payout[], isDeposit = false) => {
|
const processPayouts = async (payouts: Payout[], isDeposit = false) => {
|
||||||
|
@ -211,4 +202,34 @@ const sendResolutionEmails = async (
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getResolutionParams(outcomeType: string, body: string) {
|
||||||
|
if (outcomeType === 'NUMERIC') {
|
||||||
|
return {
|
||||||
|
...validate(numericSchema, body),
|
||||||
|
resolutions: undefined,
|
||||||
|
probabilityInt: undefined,
|
||||||
|
}
|
||||||
|
} else if (outcomeType === 'FREE_RESPONSE') {
|
||||||
|
const { outcome, resolutions } = validate(freeResponseSchema, body)
|
||||||
|
if (!(outcome === 'MKT' && resolutions))
|
||||||
|
throw new APIError(
|
||||||
|
400,
|
||||||
|
'Resolutions cannot be specified with MKT outcome'
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
outcome,
|
||||||
|
resolutions,
|
||||||
|
value: undefined,
|
||||||
|
probabilityInt: undefined,
|
||||||
|
}
|
||||||
|
} else if (outcomeType === 'BINARY') {
|
||||||
|
return {
|
||||||
|
...validate(binarySchema, body),
|
||||||
|
value: undefined,
|
||||||
|
resolutions: undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new APIError(500, `Invalid outcome type: ${outcomeType}`)
|
||||||
|
}
|
||||||
|
|
||||||
const firestore = admin.firestore()
|
const firestore = admin.firestore()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user