Add AnswerContract type to wrap FR, MCM, Bounty

This commit is contained in:
Austin Chen 2022-08-15 16:35:13 -07:00
parent 49f60cdb80
commit 00717c90ef
15 changed files with 49 additions and 34 deletions

View File

@ -24,6 +24,7 @@ import {
FreeResponseContract,
PseudoNumericContract,
MultipleChoiceContract,
AnswerContract,
} from './contract'
import { floatingEqual } from './util/math'
@ -201,9 +202,7 @@ export function getContractBetNullMetrics() {
}
}
export function getTopAnswer(
contract: FreeResponseContract | MultipleChoiceContract
) {
export function getTopAnswer(contract: AnswerContract) {
const { answers } = contract
const top = maxBy(
answers?.map((answer) => ({

View File

@ -71,6 +71,10 @@ export type DPMContract = Contract & DPM
export type CPMMContract = Contract & CPMM
export type DPMBinaryContract = BinaryContract & DPM
export type CPMMBinaryContract = BinaryContract & CPMM
export type AnswerContract =
| FreeResponseContract
| MultipleChoiceContract
| BountyContract
export type DPM = {
mechanism: 'dpm-2'

View File

@ -15,6 +15,7 @@ import {
getCpmmProbability,
} from './calculate-cpmm'
import {
AnswerContract,
CPMMBinaryContract,
DPMBinaryContract,
FreeResponseContract,
@ -323,7 +324,7 @@ export const getNewBinaryDpmBetInfo = (
export const getNewMultiBetInfo = (
outcome: string,
amount: number,
contract: FreeResponseContract | MultipleChoiceContract,
contract: AnswerContract,
loanAmount: number
) => {
const { pool, totalShares, totalBets } = contract

View File

@ -3,6 +3,7 @@ import { sum, groupBy, sumBy, mapValues } from 'lodash'
import { Bet, NumericBet } from './bet'
import { deductDpmFees, getDpmProbability } from './calculate-dpm'
import {
AnswerContract,
DPMContract,
FreeResponseContract,
MultipleChoiceContract,
@ -184,7 +185,7 @@ export const getDpmMktPayouts = (
export const getPayoutsMultiOutcome = (
resolutions: { [outcome: string]: number },
contract: FreeResponseContract | MultipleChoiceContract,
contract: AnswerContract,
bets: Bet[]
) => {
const poolTotal = sum(Object.values(contract.pool))

View File

@ -36,7 +36,10 @@ export const createanswer = newEndpoint(opts, async (req, auth) => {
if (!contractSnap.exists) throw new APIError(400, 'Invalid contract')
const contract = contractSnap.data() as Contract
if (contract.outcomeType !== 'FREE_RESPONSE')
if (
contract.outcomeType !== 'FREE_RESPONSE' &&
contract.outcomeType !== 'BOUNTY'
)
throw new APIError(400, 'Requires a free response contract')
const { closeTime, volume } = contract

View File

@ -3,6 +3,7 @@ import { z } from 'zod'
import { difference, uniq, mapValues, groupBy, sumBy } from 'lodash'
import {
AnswerContract,
Contract,
FreeResponseContract,
MultipleChoiceContract,
@ -295,10 +296,7 @@ function getResolutionParams(contract: Contract, body: string) {
throw new APIError(500, `Invalid outcome type: ${outcomeType}`)
}
function validateAnswer(
contract: FreeResponseContract | MultipleChoiceContract,
answer: number
) {
function validateAnswer(contract: AnswerContract, answer: number) {
const validIds = contract.answers.map((a) => a.id)
if (!validIds.includes(answer.toString())) {
throw new APIError(400, `${answer} is not a valid answer ID`)

View File

@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react'
import { XIcon } from '@heroicons/react/solid'
import { Answer } from 'common/answer'
import { FreeResponseContract, MultipleChoiceContract } from 'common/contract'
import { AnswerContract } from 'common/contract'
import { BuyAmountInput } from '../amount-input'
import { Col } from '../layout/col'
import { APIError, placeBet } from 'web/lib/firebase/api'
@ -30,7 +30,7 @@ import { AlertBox } from '../alert-box'
export function AnswerBetPanel(props: {
answer: Answer
contract: FreeResponseContract | MultipleChoiceContract
contract: AnswerContract
closePanel: () => void
className?: string
isModal?: boolean

View File

@ -1,7 +1,7 @@
import clsx from 'clsx'
import { Answer } from 'common/answer'
import { FreeResponseContract, MultipleChoiceContract } from 'common/contract'
import { AnswerContract } from 'common/contract'
import { Col } from '../layout/col'
import { Row } from '../layout/row'
import { Avatar } from '../avatar'
@ -13,7 +13,7 @@ import { Linkify } from '../linkify'
export function AnswerItem(props: {
answer: Answer
contract: FreeResponseContract | MultipleChoiceContract
contract: AnswerContract
showChoice: 'radio' | 'checkbox' | undefined
chosenProb: number | undefined
totalChosenProb?: number

View File

@ -2,7 +2,7 @@ import clsx from 'clsx'
import { sum } from 'lodash'
import { useState } from 'react'
import { FreeResponseContract, MultipleChoiceContract } from 'common/contract'
import { AnswerContract } from 'common/contract'
import { Col } from '../layout/col'
import { APIError, resolveMarket } from 'web/lib/firebase/api'
import { Row } from '../layout/row'
@ -11,7 +11,7 @@ import { ResolveConfirmationButton } from '../confirmation-button'
import { removeUndefinedProps } from 'common/util/object'
export function AnswerResolvePanel(props: {
contract: FreeResponseContract | MultipleChoiceContract
contract: AnswerContract
resolveOption: 'CHOOSE' | 'CHOOSE_MULTIPLE' | 'CANCEL' | undefined
setResolveOption: (
option: 'CHOOSE' | 'CHOOSE_MULTIPLE' | 'CANCEL' | undefined

View File

@ -5,14 +5,18 @@ import { groupBy, sortBy, sumBy } from 'lodash'
import { memo } from 'react'
import { Bet } from 'common/bet'
import { FreeResponseContract, MultipleChoiceContract } from 'common/contract'
import {
AnswerContract,
FreeResponseContract,
MultipleChoiceContract,
} from 'common/contract'
import { getOutcomeProbability } from 'common/calculate'
import { useWindowSize } from 'web/hooks/use-window-size'
const NUM_LINES = 6
export const AnswersGraph = memo(function AnswersGraph(props: {
contract: FreeResponseContract | MultipleChoiceContract
contract: AnswerContract
bets: Bet[]
height?: number
}) {
@ -178,10 +182,7 @@ function formatTime(
return d.format(format)
}
const computeProbsByOutcome = (
bets: Bet[],
contract: FreeResponseContract | MultipleChoiceContract
) => {
const computeProbsByOutcome = (bets: Bet[], contract: AnswerContract) => {
const { totalBets, outcomeType } = contract
const betsByOutcome = groupBy(bets, (bet) => bet.outcome)

View File

@ -1,7 +1,12 @@
import { sortBy, partition, sum, uniq } from 'lodash'
import { useEffect, useState } from 'react'
import { FreeResponseContract, MultipleChoiceContract } from 'common/contract'
import {
AnswerContract,
BountyContract,
FreeResponseContract,
MultipleChoiceContract,
} from 'common/contract'
import { Col } from '../layout/col'
import { useUser } from 'web/hooks/use-user'
import { getDpmOutcomeProbability } from 'common/calculate-dpm'
@ -25,9 +30,7 @@ import { UserLink } from 'web/components/user-page'
import { Linkify } from 'web/components/linkify'
import { BuyButton } from 'web/components/yes-no-selector'
export function AnswersPanel(props: {
contract: FreeResponseContract | MultipleChoiceContract
}) {
export function AnswersPanel(props: { contract: AnswerContract }) {
const { contract } = props
const { creatorId, resolution, resolutions, totalBets, outcomeType } =
contract
@ -136,7 +139,7 @@ export function AnswersPanel(props: {
<div className="pb-4 text-gray-500">No answers yet...</div>
)}
{outcomeType === 'FREE_RESPONSE' &&
{(outcomeType === 'FREE_RESPONSE' || outcomeType === 'BOUNTY') &&
tradingAllowed(contract) &&
(!resolveOption || resolveOption === 'CANCEL') && (
<CreateAnswerPanel contract={contract} />
@ -158,7 +161,7 @@ export function AnswersPanel(props: {
}
function getAnswerItems(
contract: FreeResponseContract | MultipleChoiceContract,
contract: AnswerContract,
answers: Answer[],
user: User | undefined | null
) {
@ -184,7 +187,7 @@ function getAnswerItems(
}
function OpenAnswer(props: {
contract: FreeResponseContract | MultipleChoiceContract
contract: AnswerContract
answer: Answer
items: ActivityItem[]
type: string

View File

@ -3,7 +3,7 @@ import { useState } from 'react'
import Textarea from 'react-expanding-textarea'
import { findBestMatch } from 'string-similarity'
import { FreeResponseContract } from 'common/contract'
import { BountyContract, FreeResponseContract } from 'common/contract'
import { BuyAmountInput } from '../amount-input'
import { Col } from '../layout/col'
import { APIError, createAnswer } from 'web/lib/firebase/api'
@ -26,7 +26,9 @@ import { MAX_ANSWER_LENGTH } from 'common/answer'
import { withTracking } from 'web/lib/service/analytics'
import { lowerCase } from 'lodash'
export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
export function CreateAnswerPanel(props: {
contract: FreeResponseContract | BountyContract
}) {
const { contract } = props
const user = useUser()
const [text, setText] = useState('')

View File

@ -9,6 +9,7 @@ import {
import { contractPath, getBinaryProbPercent } from 'web/lib/firebase/contracts'
import { Col } from '../layout/col'
import {
AnswerContract,
BinaryContract,
BountyContract,
Contract,
@ -232,7 +233,7 @@ export function BountyValue(props: {
}
function FreeResponseTopAnswer(props: {
contract: FreeResponseContract | MultipleChoiceContract
contract: AnswerContract
truncate: 'short' | 'long' | 'none'
className?: string
}) {

View File

@ -56,7 +56,8 @@ export function ContractTabs(props: {
tips={tips}
user={user}
mode={
contract.outcomeType === 'FREE_RESPONSE'
contract.outcomeType === 'FREE_RESPONSE' ||
contract.outcomeType === 'BOUNTY'
? 'free-response-comment-answer-groups'
: 'comments'
}

View File

@ -3,6 +3,7 @@ import { Answer } from 'common/answer'
import { getProbability } from 'common/calculate'
import { getValueFromBucket } from 'common/calculate-dpm'
import {
AnswerContract,
BinaryContract,
BountyContract,
Contract,
@ -78,7 +79,7 @@ export function BinaryContractOutcomeLabel(props: {
}
export function FreeResponseOutcomeLabel(props: {
contract: FreeResponseContract | MultipleChoiceContract | BountyContract
contract: AnswerContract
resolution: string | 'CANCEL' | 'MKT'
truncate: 'short' | 'long' | 'none'
answerClassName?: string