diff --git a/common/answer.ts b/common/answer.ts index 344d382f..9e63d60c 100644 --- a/common/answer.ts +++ b/common/answer.ts @@ -1,3 +1,5 @@ +import { User } from './user' + export type Answer = { id: string contractId: string @@ -10,3 +12,18 @@ export type Answer = { text: string } + +export const getNoneAnswer = (contractId: string, creator: User) => { + const { username, name, avatarUrl } = creator + + return { + id: 'NONE', + contractId, + createdTime: Date.now(), + userId: creator.id, + username, + name, + avatarUrl, + text: 'None', + } +} diff --git a/common/antes.ts b/common/antes.ts index f123e882..0bddf33c 100644 --- a/common/antes.ts +++ b/common/antes.ts @@ -67,7 +67,9 @@ export function getFreeAnswerAnte( contract: Contract<'MULTI'>, anteBetId: string ) { - const ante = contract.totalBets.YES + contract.totalBets.NO + const { totalBets, totalShares } = contract + const amount = totalBets.NONE + const shares = totalShares.NONE const { createdTime } = contract @@ -75,11 +77,11 @@ export function getFreeAnswerAnte( id: anteBetId, userId: creator.id, contractId: contract.id, - amount: ante, - shares: 0, + amount, + shares, outcome: 'NONE', - probBefore: 0, - probAfter: 0, + probBefore: 100, + probAfter: 100, createdTime, isAnte: true, } diff --git a/common/calculate.ts b/common/calculate.ts index 82944e0b..84259973 100644 --- a/common/calculate.ts +++ b/common/calculate.ts @@ -1,4 +1,4 @@ -import _ from 'lodash' +import * as _ from 'lodash' import { Bet } from './bet' import { Contract } from './contract' import { FEES } from './fees' diff --git a/common/new-contract.ts b/common/new-contract.ts index edb38d72..edfa976c 100644 --- a/common/new-contract.ts +++ b/common/new-contract.ts @@ -24,7 +24,7 @@ export function getNewContract( const propsByOutcomeType = outcomeType === 'BINARY' ? getBinaryProps(initialProb, ante) - : getFreeAnswerProps() + : getFreeAnswerProps(ante) const contract: Contract<'BINARY' | 'MULTI'> = removeUndefinedProps({ id, @@ -68,11 +68,11 @@ const getBinaryProps = (initialProb: number, ante: number) => { } } -const getFreeAnswerProps = () => { +const getFreeAnswerProps = (ante: number) => { return { - pool: {}, - totalShares: {}, - totalBets: {}, + pool: { NONE: ante }, + totalShares: { NONE: ante }, + totalBets: { NONE: ante }, phantomShares: undefined, outcomes: 'FREE_ANSWER' as const, } diff --git a/functions/src/create-contract.ts b/functions/src/create-contract.ts index f362157b..74558ff1 100644 --- a/functions/src/create-contract.ts +++ b/functions/src/create-contract.ts @@ -11,6 +11,7 @@ import { getFreeAnswerAnte, MINIMUM_ANTE, } from '../../common/antes' +import { getNoneAnswer } from '../../common/answer' export const createContract = functions .runWith({ minInstances: 1 }) @@ -106,16 +107,21 @@ export const createContract = functions await yesBetDoc.set(yesBet) await noBetDoc.set(noBet) } else if (outcomeType === 'MULTI') { + const noneAnswerDoc = firestore.doc( + `contracts/${contract.id}/answers/NONE` + ) + const noneAnswer = getNoneAnswer(contract.id, creator) + await noneAnswerDoc.set(noneAnswer) + const anteBetDoc = firestore .collection(`contracts/${contract.id}/bets`) .doc() - getFreeAnswerAnte( + const anteBet = getFreeAnswerAnte( creator, contract as Contract<'MULTI'>, anteBetDoc.id ) - // Disable until we figure out how this should work. - // await anteBetDoc.set(anteBetDoc) + await anteBetDoc.set(anteBet) } } diff --git a/web/components/answers-panel.tsx b/web/components/answers-panel.tsx index 771be5be..b0d7b628 100644 --- a/web/components/answers-panel.tsx +++ b/web/components/answers-panel.tsx @@ -1,6 +1,7 @@ import clsx from 'clsx' import { useEffect, useRef, useState } from 'react' import Textarea from 'react-expanding-textarea' +import { XIcon } from '@heroicons/react/solid' import { Answer } from '../../common/answer' import { Contract } from '../../common/contract' @@ -25,8 +26,10 @@ import { getProbabilityAfterBet, getOutcomeProbability, calculateShares, + calculatePayoutAfterCorrectBet, } from '../../common/calculate' import { firebaseLogin } from '../lib/firebase/users' +import { Bet } from '../../common/bet' export function AnswersPanel(props: { contract: Contract<'MULTI'> @@ -49,12 +52,14 @@ function AnswerItem(props: { answer: Answer; contract: Contract<'MULTI'> }) { const { username, avatarUrl, name, createdTime } = answer const createdDate = dayjs(createdTime).format('MMM D') + const prob = getOutcomeProbability(contract.totalShares, answer.id) + const probPercent = formatPercent(prob) const [isBetting, setIsBetting] = useState(false) return ( - - + +
{answer.text}
@@ -76,15 +81,26 @@ function AnswerItem(props: { answer: Answer; contract: Contract<'MULTI'> }) {
- { - setIsBetting(true) - }} - /> - + {!isBetting && ( + +
{probPercent}
+ { + setIsBetting(true) + }} + /> + + )} + - {isBetting && } + {isBetting && ( + setIsBetting(false)} + /> + )} ) } @@ -92,8 +108,9 @@ function AnswerItem(props: { answer: Answer; contract: Contract<'MULTI'> }) { function AnswerBetPanel(props: { answer: Answer contract: Contract<'MULTI'> + closePanel: () => void }) { - const { answer, contract } = props + const { answer, contract, closePanel } = props const { id: answerId } = answer const user = useUser() @@ -101,18 +118,12 @@ function AnswerBetPanel(props: { const [error, setError] = useState() const [isSubmitting, setIsSubmitting] = useState(false) - const [wasSubmitted, setWasSubmitted] = useState(false) const inputRef = useRef(null) useEffect(() => { inputRef.current && inputRef.current.focus() }, []) - function onBetChange(newAmount: number | undefined) { - setWasSubmitted(false) - setBetAmount(newAmount) - } - async function submitBet() { if (!user || !betAmount) return @@ -133,9 +144,7 @@ function AnswerBetPanel(props: { console.log('placed bet. Result:', result) if (result?.status === 'success') { - setIsSubmitting(false) - setWasSubmitted(true) - setBetAmount(undefined) + closePanel() } else { setError(result?.error || 'Error placing bet') setIsSubmitting(false) @@ -155,83 +164,86 @@ function AnswerBetPanel(props: { const shares = calculateShares(contract.totalShares, betAmount ?? 0, answerId) const currentPayout = betAmount - ? 0 - : // calculatePayoutAfterCorrectBet(contract, { - // outcome: answerId, - // amount: betAmount, - // shares, - // } as Bet) - 0 + ? calculatePayoutAfterCorrectBet( + contract as any as Contract, + { + outcome: answerId, + amount: betAmount, + shares, + } as Bet + ) + : 0 const currentReturn = betAmount ? (currentPayout - betAmount) / betAmount : 0 const currentReturnPercent = (currentReturn * 100).toFixed() + '%' return ( - - -
Amount
- + +
Buy this answer
+ + +
+
Amount
+ + + + +
Implied probability
+ +
{formatPercent(initialProb)}
+
+
{formatPercent(resultProb)}
+
+ + + + + Potential payout + + +
+ {formatMoney(currentPayout)} +   (+{currentReturnPercent}) +
- + -
- Implied probability -
- -
{formatPercent(initialProb)}
-
-
{formatPercent(resultProb)}
-
- - - - - Potential payout - - -
- {formatMoney(currentPayout)} -   (+{currentReturnPercent}) -
- - - - {user ? ( - - ) : ( - - )} - - {wasSubmitted &&
Trade submitted!
} - + {user ? ( + + ) : ( + + )} ) } @@ -267,7 +279,7 @@ function CreateAnswerInput(props: { contract: Contract<'MULTI'> }) { } return ( - +
Add your answer
diff --git a/web/pages/[username]/[contractSlug].tsx b/web/pages/[username]/[contractSlug].tsx index dc5e3956..0798407a 100644 --- a/web/pages/[username]/[contractSlug].tsx +++ b/web/pages/[username]/[contractSlug].tsx @@ -121,10 +121,13 @@ export default function ContractPage(props: { folds={folds} > {contract.outcomes === 'FREE_ANSWER' && ( - + <> + + + )}