diff --git a/common/calculate.ts b/common/calculate.ts index d25fd313..9b2d39be 100644 --- a/common/calculate.ts +++ b/common/calculate.ts @@ -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) => ({ diff --git a/common/contract.ts b/common/contract.ts index cad5e4b7..3120e16f 100644 --- a/common/contract.ts +++ b/common/contract.ts @@ -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' diff --git a/common/new-bet.ts b/common/new-bet.ts index 576f35f8..c1cc386f 100644 --- a/common/new-bet.ts +++ b/common/new-bet.ts @@ -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 diff --git a/common/payouts-dpm.ts b/common/payouts-dpm.ts index 7d4a0185..75cb5156 100644 --- a/common/payouts-dpm.ts +++ b/common/payouts-dpm.ts @@ -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)) diff --git a/functions/src/create-answer.ts b/functions/src/create-answer.ts index 2abaf44d..cb936d9f 100644 --- a/functions/src/create-answer.ts +++ b/functions/src/create-answer.ts @@ -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 diff --git a/functions/src/resolve-market.ts b/functions/src/resolve-market.ts index 7277f40b..b7faec78 100644 --- a/functions/src/resolve-market.ts +++ b/functions/src/resolve-market.ts @@ -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`) diff --git a/web/components/answers/answer-bet-panel.tsx b/web/components/answers/answer-bet-panel.tsx index 238c7783..74595d0d 100644 --- a/web/components/answers/answer-bet-panel.tsx +++ b/web/components/answers/answer-bet-panel.tsx @@ -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 diff --git a/web/components/answers/answer-item.tsx b/web/components/answers/answer-item.tsx index f1ab2f88..2049630f 100644 --- a/web/components/answers/answer-item.tsx +++ b/web/components/answers/answer-item.tsx @@ -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 diff --git a/web/components/answers/answer-resolve-panel.tsx b/web/components/answers/answer-resolve-panel.tsx index 0a4ac1e1..8d39b0b0 100644 --- a/web/components/answers/answer-resolve-panel.tsx +++ b/web/components/answers/answer-resolve-panel.tsx @@ -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 diff --git a/web/components/answers/answers-graph.tsx b/web/components/answers/answers-graph.tsx index 27152db9..d85a85bc 100644 --- a/web/components/answers/answers-graph.tsx +++ b/web/components/answers/answers-graph.tsx @@ -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) diff --git a/web/components/answers/answers-panel.tsx b/web/components/answers/answers-panel.tsx index 6e0bfef6..4b76d96b 100644 --- a/web/components/answers/answers-panel.tsx +++ b/web/components/answers/answers-panel.tsx @@ -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: {