diff --git a/common/contract.ts b/common/contract.ts index 8e3723b7..cad5e4b7 100644 --- a/common/contract.ts +++ b/common/contract.ts @@ -18,6 +18,7 @@ export type AnyContractType = | (DPM & FreeResponse) | (DPM & Numeric) | (DPM & MultipleChoice) + | (DPM & Bounty) export type Contract = { id: string @@ -65,11 +66,11 @@ export type PseudoNumericContract = Contract & PseudoNumeric export type NumericContract = Contract & Numeric export type FreeResponseContract = Contract & FreeResponse export type MultipleChoiceContract = Contract & MultipleChoice +export type BountyContract = Contract & Bounty export type DPMContract = Contract & DPM export type CPMMContract = Contract & CPMM export type DPMBinaryContract = BinaryContract & DPM export type CPMMBinaryContract = BinaryContract & CPMM -export type BountyContract = Contract & Bounty export type DPM = { mechanism: 'dpm-2' @@ -133,12 +134,15 @@ export type Bounty = { outcomeType: 'BOUNTY' // One answer for each submission answers: Answer[] + // Resolution = which answer id the bounty went to + resolution?: string | 'MKT' | 'CANCEL' + resolutions?: { [outcome: string]: number } // Used for MKT resolution + + // Amount contributed to each bounty, and by who prizes: { [giverId: string]: number } prizeTotal: number - // Resolution = which ID the bounty went to - resolution?: string | 'MKT' | 'CANCEL' } export type outcomeType = AnyOutcomeType['outcomeType'] diff --git a/common/new-contract.ts b/common/new-contract.ts index ad7dc5a2..7b681ccd 100644 --- a/common/new-contract.ts +++ b/common/new-contract.ts @@ -1,6 +1,7 @@ import { range } from 'lodash' import { Binary, + Bounty, Contract, CPMM, DPM, @@ -45,16 +46,22 @@ export function getNewContract( ) const lowercaseTags = tags.map((tag) => tag.toLowerCase()) - const propsByOutcomeType = - outcomeType === 'BINARY' - ? getBinaryCpmmProps(initialProb, ante) // getBinaryDpmProps(initialProb, ante) - : outcomeType === 'PSEUDO_NUMERIC' - ? getPseudoNumericCpmmProps(initialProb, ante, min, max, isLogScale) - : outcomeType === 'NUMERIC' - ? getNumericProps(ante, bucketCount, min, max) - : outcomeType === 'MULTIPLE_CHOICE' - ? getMultipleChoiceProps(ante, answers) - : getFreeAnswerProps(ante) + const PROPS = { + BINARY: getBinaryCpmmProps(initialProb, ante), // getBinaryDpmProps(initialProb, ante) + PSEUDO_NUMERIC: getPseudoNumericCpmmProps( + initialProb, + ante, + min, + max, + isLogScale + ), + FREE_RESPONSE: getFreeAnswerProps(ante), + MULTIPLE_CHOICE: getMultipleChoiceProps(ante, answers), + NUMERIC: getNumericProps(ante, bucketCount, min, max), + BOUNTY: getBountyProps(ante, creator), + } + + const propsByOutcomeType = PROPS[outcomeType] || PROPS['FREE_RESPONSE'] const contract: Contract = removeUndefinedProps({ id, @@ -157,6 +164,18 @@ const getFreeAnswerProps = (ante: number) => { return system } +function getBountyProps(ante: number, creator: User) { + const system: DPM & Bounty = { + ...getFreeAnswerProps(ante), + outcomeType: 'BOUNTY', + prizes: { + [creator.id]: ante, + }, + prizeTotal: ante, + } + return system +} + const getMultipleChoiceProps = (ante: number, answers: string[]) => { const numAnswers = answers.length const betAnte = ante / numAnswers diff --git a/web/components/contract/contract-card.tsx b/web/components/contract/contract-card.tsx index b4f20a40..562a2bdc 100644 --- a/web/components/contract/contract-card.tsx +++ b/web/components/contract/contract-card.tsx @@ -1,11 +1,16 @@ import clsx from 'clsx' import Link from 'next/link' import { Row } from '../layout/row' -import { formatLargeNumber, formatPercent } from 'common/util/format' +import { + formatLargeNumber, + formatMoney, + formatPercent, +} from 'common/util/format' import { contractPath, getBinaryProbPercent } from 'web/lib/firebase/contracts' import { Col } from '../layout/col' import { BinaryContract, + BountyContract, Contract, FreeResponseContract, MultipleChoiceContract, @@ -160,6 +165,13 @@ export function ContractCard(props: { truncate="long" /> )} + + {outcomeType === 'BOUNTY' && ( + + )} )} @@ -202,6 +214,23 @@ export function BinaryResolutionOrChance(props: { ) } +export function BountyValue(props: { + contract: BountyContract + large?: boolean + className?: string +}) { + const { contract, large, className } = props + const textColor = `text-${getColor(contract)}` + return ( + +
{formatMoney(contract.prizeTotal)}
+
+ bounty +
+ + ) +} + function FreeResponseTopAnswer(props: { contract: FreeResponseContract | MultipleChoiceContract truncate: 'short' | 'long' | 'none' diff --git a/web/components/contract/contract-info-dialog.tsx b/web/components/contract/contract-info-dialog.tsx index 168ada50..3871673c 100644 --- a/web/components/contract/contract-info-dialog.tsx +++ b/web/components/contract/contract-info-dialog.tsx @@ -32,14 +32,15 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) { 'userId' ).length - const typeDisplay = - outcomeType === 'BINARY' - ? 'YES / NO' - : outcomeType === 'FREE_RESPONSE' - ? 'Free response' - : outcomeType === 'MULTIPLE_CHOICE' - ? 'Multiple choice' - : 'Numeric' + const TYPES = { + BINARY: 'YES / NO', + FREE_RESPONSE: 'Free Response', + MULTIPLE_CHOICE: 'Multiple Choice', + NUMERIC: 'Numeric (deprecated)', + PSEUDO_NUMERIC: 'Numeric', + BOUNTY: 'Bounty', + } + const typeDisplay = TYPES[outcomeType] || 'Unknown' return ( <> diff --git a/web/components/contract/contract-overview.tsx b/web/components/contract/contract-overview.tsx index b95bb02b..83bdccc3 100644 --- a/web/components/contract/contract-overview.tsx +++ b/web/components/contract/contract-overview.tsx @@ -10,6 +10,7 @@ import { Row } from '../layout/row' import { Linkify } from '../linkify' import { BinaryResolutionOrChance, + BountyValue, FreeResponseResolutionOrChance, NumericResolutionOrExpectation, PseudoNumericResolutionOrExpectation, @@ -44,7 +45,6 @@ export const ContractOverview = (props: {
- {isBinary && ( )} - {isPseudoNumeric && ( )} - {outcomeType === 'NUMERIC' && ( )} + {outcomeType === 'BOUNTY' && ( + + )} {isBinary ? ( diff --git a/web/components/outcome-label.tsx b/web/components/outcome-label.tsx index 85e171d8..b49ccdf9 100644 --- a/web/components/outcome-label.tsx +++ b/web/components/outcome-label.tsx @@ -4,6 +4,7 @@ import { getProbability } from 'common/calculate' import { getValueFromBucket } from 'common/calculate-dpm' import { BinaryContract, + BountyContract, Contract, FreeResponseContract, MultipleChoiceContract, @@ -77,7 +78,7 @@ export function BinaryContractOutcomeLabel(props: { } export function FreeResponseOutcomeLabel(props: { - contract: FreeResponseContract | MultipleChoiceContract + contract: FreeResponseContract | MultipleChoiceContract | BountyContract resolution: string | 'CANCEL' | 'MKT' truncate: 'short' | 'long' | 'none' answerClassName?: string