import clsx from 'clsx' import { getOutcomeProbability, getTopAnswer } from 'common/calculate' import { getExpectedValue } from 'common/calculate-dpm' import { Contract, FullContract, CPMM, DPM, Binary, NumericContract, FreeResponse, } from 'common/contract' import { formatMoney } from 'common/util/format' import toast from 'react-hot-toast' import { useUser } from 'web/hooks/use-user' import { useUserContractBets } from 'web/hooks/use-user-bets' import { placeBet } from 'web/lib/firebase/api-call' import { getBinaryProb } from 'web/lib/firebase/contracts' import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon' import TriangleFillIcon from 'web/lib/icons/triangle-fill-icon' import { Col } from '../layout/col' import { OUTCOME_TO_COLOR } from '../outcome-label' import { useSaveShares } from '../use-save-shares' import { BinaryResolutionOrChance, NumericResolutionOrExpectation, FreeResponseResolutionOrChance, } from './contract-card' export function QuickBet(props: { contract: Contract }) { const { contract } = props const user = useUser() const userBets = useUserContractBets(user?.id, contract.id) const { yesFloorShares, noFloorShares, yesShares, noShares } = useSaveShares( contract as FullContract, userBets ) // TODO: For some reason, Floor Shares are inverted for non-BINARY markets const hasUpShares = contract.outcomeType === 'BINARY' ? yesFloorShares : noFloorShares const hasDownShares = contract.outcomeType === 'BINARY' ? noFloorShares : yesFloorShares const color = getColor(contract) async function placeQuickBet(direction: 'UP' | 'DOWN') { const betPromise = async () => { const outcome = quickOutcome(contract, direction) return await placeBet({ amount: 10, outcome, contractId: contract.id, }) } const shortQ = contract.question.slice(0, 20) toast.promise(betPromise(), { loading: `${formatMoney(10)} on "${shortQ}"...`, success: `${formatMoney(10)} on "${shortQ}"...`, error: (err) => `${err.message}`, }) } function quickOutcome(contract: Contract, direction: 'UP' | 'DOWN') { if (contract.outcomeType === 'BINARY') { return direction === 'UP' ? 'YES' : 'NO' } if (contract.outcomeType === 'FREE_RESPONSE') { // TODO: Implement shorting of free response answers if (direction === 'DOWN') { throw new Error("Can't short free response answers") } return getTopAnswer(contract)?.id } if (contract.outcomeType === 'NUMERIC') { // TODO: Ideally an 'UP' bet would be a uniform bet between [current, max] throw new Error("Can't quick bet on numeric markets") } } return ( {/* Up bet triangle */}
placeQuickBet('UP')} >
{formatMoney(10)}
{hasUpShares > 0 ? ( ) : ( )}
{/* Down bet triangle */}
placeQuickBet('DOWN')} >
{hasDownShares > 0 ? ( ) : ( )}
{formatMoney(10)}
) } export function ProbBar(props: { contract: Contract }) { const { contract } = props const color = getColor(contract) const prob = getProb(contract) return ( <>
) } export function QuickOutcomeView(props: { contract: Contract }) { const { contract } = props const { outcomeType } = contract return ( <> {outcomeType === 'BINARY' && ( )} {outcomeType === 'NUMERIC' && ( )} {outcomeType === 'FREE_RESPONSE' && ( } truncate="long" hideText /> )} ) } // Return a number from 0 to 1 for this contract // Resolved contracts are set to 1, for coloring purposes (even if NO) function getProb(contract: Contract) { const { outcomeType, resolution } = contract return resolution ? 1 : outcomeType === 'BINARY' ? getBinaryProb(contract) : outcomeType === 'FREE_RESPONSE' ? getOutcomeProbability(contract, getTopAnswer(contract)?.id || '') : outcomeType === 'NUMERIC' ? getNumericScale(contract as NumericContract) : 1 // Should not happen } function getNumericScale(contract: NumericContract) { const { min, max } = contract const ev = getExpectedValue(contract) return (ev - min) / (max - min) } export function getColor(contract: Contract) { // TODO: Not sure why eg green-400 doesn't work here; try upgrading Tailwind // TODO: Try injecting a gradient here // return 'primary' const { resolution } = contract if (resolution) { return ( // @ts-ignore; TODO: Have better typing for contract.resolution? OUTCOME_TO_COLOR[resolution] || // If resolved to a FR answer, use 'primary' 'primary' ) } if (contract.outcomeType === 'NUMERIC') { return 'blue-400' } const marketClosed = (contract.closeTime || Infinity) < Date.now() return marketClosed ? 'gray-400' : getProb(contract) >= 0.5 ? 'primary' : 'red-400' }