pseudo numeric market layout, quick betting
This commit is contained in:
parent
0ee6dfae94
commit
d8aaf4219e
10
common/numeric.ts
Normal file
10
common/numeric.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { PseudoNumericContract } from './contract'
|
||||||
|
|
||||||
|
export function formatNumericProbability(
|
||||||
|
p: number,
|
||||||
|
contract: PseudoNumericContract
|
||||||
|
) {
|
||||||
|
const { min, max } = contract
|
||||||
|
const value = p * (max - min) + min
|
||||||
|
return Math.round(value).toString()
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ import { APIError, placeBet } from 'web/lib/firebase/api-call'
|
||||||
import { sellShares } from 'web/lib/firebase/api-call'
|
import { sellShares } from 'web/lib/firebase/api-call'
|
||||||
import { AmountInput, BuyAmountInput } from './amount-input'
|
import { AmountInput, BuyAmountInput } from './amount-input'
|
||||||
import { InfoTooltip } from './info-tooltip'
|
import { InfoTooltip } from './info-tooltip'
|
||||||
import { BinaryOutcomeLabel } from './outcome-label'
|
import { BinaryOutcomeLabel, PseudoNumericOutcomeLabel } from './outcome-label'
|
||||||
import {
|
import {
|
||||||
calculatePayoutAfterCorrectBet,
|
calculatePayoutAfterCorrectBet,
|
||||||
calculateShares,
|
calculateShares,
|
||||||
|
|
@ -46,7 +46,7 @@ import { isIOS } from 'web/lib/util/device'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function BetPanel(props: {
|
export function BetPanel(props: {
|
||||||
contract: BinaryContract
|
contract: BinaryContract | PseudoNumericContract
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { contract, className } = props
|
const { contract, className } = props
|
||||||
|
|
@ -85,7 +85,7 @@ export function BetPanel(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BetPanelSwitcher(props: {
|
export function BetPanelSwitcher(props: {
|
||||||
contract: BinaryContract
|
contract: BinaryContract | PseudoNumericContract
|
||||||
className?: string
|
className?: string
|
||||||
title?: string // Set if BetPanel is on a feed modal
|
title?: string // Set if BetPanel is on a feed modal
|
||||||
selected?: 'YES' | 'NO'
|
selected?: 'YES' | 'NO'
|
||||||
|
|
@ -93,7 +93,8 @@ export function BetPanelSwitcher(props: {
|
||||||
}) {
|
}) {
|
||||||
const { contract, className, title, selected, onBetSuccess } = props
|
const { contract, className, title, selected, onBetSuccess } = props
|
||||||
|
|
||||||
const { mechanism } = contract
|
const { mechanism, outcomeType } = contract
|
||||||
|
const isPseudoNumeric = outcomeType === 'PSEUDO_NUMERIC'
|
||||||
|
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const userBets = useUserContractBets(user?.id, contract.id)
|
const userBets = useUserContractBets(user?.id, contract.id)
|
||||||
|
|
@ -126,7 +127,12 @@ export function BetPanelSwitcher(props: {
|
||||||
<Row className="items-center justify-between gap-2">
|
<Row className="items-center justify-between gap-2">
|
||||||
<div>
|
<div>
|
||||||
You have {formatWithCommas(floorShares)}{' '}
|
You have {formatWithCommas(floorShares)}{' '}
|
||||||
<BinaryOutcomeLabel outcome={sharesOutcome} /> shares
|
{isPseudoNumeric ? (
|
||||||
|
<PseudoNumericOutcomeLabel outcome={sharesOutcome} />
|
||||||
|
) : (
|
||||||
|
<BinaryOutcomeLabel outcome={sharesOutcome} />
|
||||||
|
)}{' '}
|
||||||
|
shares
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{tradeType === 'BUY' && (
|
{tradeType === 'BUY' && (
|
||||||
|
|
@ -405,7 +411,7 @@ function BuyPanel(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SellPanel(props: {
|
export function SellPanel(props: {
|
||||||
contract: CPMMBinaryContract
|
contract: CPMMBinaryContract | PseudoNumericContract
|
||||||
userBets: Bet[]
|
userBets: Bet[]
|
||||||
shares: number
|
shares: number
|
||||||
sharesOutcome: 'YES' | 'NO'
|
sharesOutcome: 'YES' | 'NO'
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import clsx from 'clsx'
|
||||||
|
|
||||||
import { BetPanelSwitcher } from './bet-panel'
|
import { BetPanelSwitcher } from './bet-panel'
|
||||||
import { YesNoSelector } from './yes-no-selector'
|
import { YesNoSelector } from './yes-no-selector'
|
||||||
import { BinaryContract } from 'common/contract'
|
import { BinaryContract, PseudoNumericContract } from 'common/contract'
|
||||||
import { Modal } from './layout/modal'
|
import { Modal } from './layout/modal'
|
||||||
import { SellButton } from './sell-button'
|
import { SellButton } from './sell-button'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
|
|
@ -12,7 +12,7 @@ import { useSaveShares } from './use-save-shares'
|
||||||
|
|
||||||
// Inline version of a bet panel. Opens BetPanel in a new modal.
|
// Inline version of a bet panel. Opens BetPanel in a new modal.
|
||||||
export default function BetRow(props: {
|
export default function BetRow(props: {
|
||||||
contract: BinaryContract
|
contract: BinaryContract | PseudoNumericContract
|
||||||
className?: string
|
className?: string
|
||||||
btnClassName?: string
|
btnClassName?: string
|
||||||
betPanelClassName?: string
|
betPanelClassName?: string
|
||||||
|
|
@ -32,6 +32,7 @@ export default function BetRow(props: {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<YesNoSelector
|
<YesNoSelector
|
||||||
|
isPseudoNumeric={contract.outcomeType === 'PSEUDO_NUMERIC'}
|
||||||
className={clsx('justify-end', className)}
|
className={clsx('justify-end', className)}
|
||||||
btnClassName={clsx('btn-sm w-24', btnClassName)}
|
btnClassName={clsx('btn-sm w-24', btnClassName)}
|
||||||
onSelect={(choice) => {
|
onSelect={(choice) => {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
BinaryContract,
|
BinaryContract,
|
||||||
FreeResponseContract,
|
FreeResponseContract,
|
||||||
NumericContract,
|
NumericContract,
|
||||||
|
PseudoNumericContract,
|
||||||
} from 'common/contract'
|
} from 'common/contract'
|
||||||
import {
|
import {
|
||||||
AnswerLabel,
|
AnswerLabel,
|
||||||
|
|
@ -16,7 +17,11 @@ import {
|
||||||
CancelLabel,
|
CancelLabel,
|
||||||
FreeResponseOutcomeLabel,
|
FreeResponseOutcomeLabel,
|
||||||
} from '../outcome-label'
|
} from '../outcome-label'
|
||||||
import { getOutcomeProbability, getTopAnswer } from 'common/calculate'
|
import {
|
||||||
|
getOutcomeProbability,
|
||||||
|
getProbability,
|
||||||
|
getTopAnswer,
|
||||||
|
} from 'common/calculate'
|
||||||
import { AvatarDetails, MiscDetails, ShowTime } from './contract-details'
|
import { AvatarDetails, MiscDetails, ShowTime } from './contract-details'
|
||||||
import { getExpectedValue, getValueFromBucket } from 'common/calculate-dpm'
|
import { getExpectedValue, getValueFromBucket } from 'common/calculate-dpm'
|
||||||
import { QuickBet, ProbBar, getColor } from './quick-bet'
|
import { QuickBet, ProbBar, getColor } from './quick-bet'
|
||||||
|
|
@ -24,6 +29,7 @@ import { useContractWithPreload } from 'web/hooks/use-contract'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { track } from '@amplitude/analytics-browser'
|
import { track } from '@amplitude/analytics-browser'
|
||||||
import { trackCallback } from 'web/lib/service/analytics'
|
import { trackCallback } from 'web/lib/service/analytics'
|
||||||
|
import { formatNumericProbability } from 'common/numeric'
|
||||||
|
|
||||||
export function ContractCard(props: {
|
export function ContractCard(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
|
@ -284,3 +290,37 @@ export function NumericResolutionOrExpectation(props: {
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PseudoNumericResolutionOrExpectation(props: {
|
||||||
|
contract: PseudoNumericContract
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const { contract, className } = props
|
||||||
|
const { resolution, resolutionProbability } = contract
|
||||||
|
const textColor = `text-blue-400`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Col className={clsx(resolution ? 'text-3xl' : 'text-xl', className)}>
|
||||||
|
{resolution ? (
|
||||||
|
<>
|
||||||
|
<div className={clsx('text-base text-gray-500')}>Resolved</div>
|
||||||
|
|
||||||
|
{resolution === 'CANCEL' ? (
|
||||||
|
<CancelLabel />
|
||||||
|
) : (
|
||||||
|
<div className="text-blue-400">
|
||||||
|
{formatNumericProbability(resolutionProbability ?? 0, contract)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className={clsx('text-3xl', textColor)}>
|
||||||
|
{formatNumericProbability(getProbability(contract), contract)}
|
||||||
|
</div>
|
||||||
|
<div className={clsx('text-base', textColor)}>expected</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
FreeResponseResolutionOrChance,
|
FreeResponseResolutionOrChance,
|
||||||
BinaryResolutionOrChance,
|
BinaryResolutionOrChance,
|
||||||
NumericResolutionOrExpectation,
|
NumericResolutionOrExpectation,
|
||||||
|
PseudoNumericResolutionOrExpectation,
|
||||||
} from './contract-card'
|
} from './contract-card'
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
import BetRow from '../bet-row'
|
import BetRow from '../bet-row'
|
||||||
|
|
@ -50,6 +51,13 @@ export const ContractOverview = (props: {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isPseudoNumeric && (
|
||||||
|
<PseudoNumericResolutionOrExpectation
|
||||||
|
contract={contract}
|
||||||
|
className="hidden items-end xl:flex"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{outcomeType === 'NUMERIC' && (
|
{outcomeType === 'NUMERIC' && (
|
||||||
<NumericResolutionOrExpectation
|
<NumericResolutionOrExpectation
|
||||||
contract={contract}
|
contract={contract}
|
||||||
|
|
@ -62,6 +70,11 @@ export const ContractOverview = (props: {
|
||||||
<Row className="items-center justify-between gap-4 xl:hidden">
|
<Row className="items-center justify-between gap-4 xl:hidden">
|
||||||
<BinaryResolutionOrChance contract={contract} />
|
<BinaryResolutionOrChance contract={contract} />
|
||||||
|
|
||||||
|
{tradingAllowed(contract) && <BetRow contract={contract} />}
|
||||||
|
</Row>
|
||||||
|
) : isPseudoNumeric ? (
|
||||||
|
<Row className="items-center justify-between gap-4 xl:hidden">
|
||||||
|
<PseudoNumericResolutionOrExpectation contract={contract} />
|
||||||
{tradingAllowed(contract) && <BetRow contract={contract} />}
|
{tradingAllowed(contract) && <BetRow contract={contract} />}
|
||||||
</Row>
|
</Row>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -81,13 +81,16 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = [{ id: 'Yes', data: points, color: '#11b981' }]
|
const isBinary = contract.outcomeType === 'BINARY'
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{ id: 'Yes', data: points, color: isBinary ? '#11b981' : '#5fa5f9' },
|
||||||
|
]
|
||||||
|
|
||||||
const multiYear = !dayjs(startDate).isSame(latestTime, 'year')
|
const multiYear = !dayjs(startDate).isSame(latestTime, 'year')
|
||||||
const lessThanAWeek = dayjs(startDate).add(8, 'day').isAfter(latestTime)
|
const lessThanAWeek = dayjs(startDate).add(8, 'day').isAfter(latestTime)
|
||||||
|
|
||||||
const formatter =
|
const formatter = isBinary ? formatPercent : formatNumeric
|
||||||
contract.outcomeType === 'BINARY' ? formatPercent : formatNumeric
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import clsx from 'clsx'
|
||||||
import {
|
import {
|
||||||
getOutcomeProbability,
|
getOutcomeProbability,
|
||||||
getOutcomeProbabilityAfterBet,
|
getOutcomeProbabilityAfterBet,
|
||||||
|
getProbability,
|
||||||
getTopAnswer,
|
getTopAnswer,
|
||||||
} from 'common/calculate'
|
} from 'common/calculate'
|
||||||
import { getExpectedValue } from 'common/calculate-dpm'
|
import { getExpectedValue } from 'common/calculate-dpm'
|
||||||
|
|
@ -25,18 +26,18 @@ import { useSaveShares } from '../use-save-shares'
|
||||||
import { sellShares } from 'web/lib/firebase/api-call'
|
import { sellShares } from 'web/lib/firebase/api-call'
|
||||||
import { calculateCpmmSale, getCpmmProbability } from 'common/calculate-cpmm'
|
import { calculateCpmmSale, getCpmmProbability } from 'common/calculate-cpmm'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
import { formatNumericProbability } from 'common/numeric'
|
||||||
|
|
||||||
const BET_SIZE = 10
|
const BET_SIZE = 10
|
||||||
|
|
||||||
export function QuickBet(props: { contract: Contract; user: User }) {
|
export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
const { contract, user } = props
|
const { contract, user } = props
|
||||||
const isCpmm = contract.mechanism === 'cpmm-1'
|
const { mechanism, outcomeType } = contract
|
||||||
|
const isCpmm = mechanism === 'cpmm-1'
|
||||||
|
|
||||||
const userBets = useUserContractBets(user.id, contract.id)
|
const userBets = useUserContractBets(user.id, contract.id)
|
||||||
const topAnswer =
|
const topAnswer =
|
||||||
contract.outcomeType === 'FREE_RESPONSE'
|
outcomeType === 'FREE_RESPONSE' ? getTopAnswer(contract) : undefined
|
||||||
? getTopAnswer(contract)
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
// TODO: yes/no from useSaveShares doesn't work on numeric contracts
|
// TODO: yes/no from useSaveShares doesn't work on numeric contracts
|
||||||
const { yesFloorShares, noFloorShares, yesShares, noShares } = useSaveShares(
|
const { yesFloorShares, noFloorShares, yesShares, noShares } = useSaveShares(
|
||||||
|
|
@ -45,9 +46,9 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
topAnswer?.number.toString() || undefined
|
topAnswer?.number.toString() || undefined
|
||||||
)
|
)
|
||||||
const hasUpShares =
|
const hasUpShares =
|
||||||
yesFloorShares || (noFloorShares && contract.outcomeType === 'NUMERIC')
|
yesFloorShares || (noFloorShares && outcomeType === 'NUMERIC')
|
||||||
const hasDownShares =
|
const hasDownShares =
|
||||||
noFloorShares && yesFloorShares <= 0 && contract.outcomeType !== 'NUMERIC'
|
noFloorShares && yesFloorShares <= 0 && outcomeType !== 'NUMERIC'
|
||||||
|
|
||||||
const [upHover, setUpHover] = useState(false)
|
const [upHover, setUpHover] = useState(false)
|
||||||
const [downHover, setDownHover] = useState(false)
|
const [downHover, setDownHover] = useState(false)
|
||||||
|
|
@ -130,25 +131,6 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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 bet against 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const textColor = `text-${getColor(contract)}`
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col
|
<Col
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|
@ -173,14 +155,14 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
<TriangleFillIcon
|
<TriangleFillIcon
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'mx-auto h-5 w-5',
|
'mx-auto h-5 w-5',
|
||||||
upHover ? textColor : 'text-gray-400'
|
upHover ? 'text-green-500' : 'text-gray-400'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TriangleFillIcon
|
<TriangleFillIcon
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'mx-auto h-5 w-5',
|
'mx-auto h-5 w-5',
|
||||||
upHover ? textColor : 'text-gray-200'
|
upHover ? 'text-green-500' : 'text-gray-200'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -189,7 +171,7 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
<QuickOutcomeView contract={contract} previewProb={previewProb} />
|
<QuickOutcomeView contract={contract} previewProb={previewProb} />
|
||||||
|
|
||||||
{/* Down bet triangle */}
|
{/* Down bet triangle */}
|
||||||
{contract.outcomeType !== 'BINARY' ? (
|
{outcomeType !== 'BINARY' && outcomeType !== 'PSEUDO_NUMERIC' ? (
|
||||||
<div>
|
<div>
|
||||||
<div className="peer absolute bottom-0 left-0 right-0 h-[50%] cursor-default"></div>
|
<div className="peer absolute bottom-0 left-0 right-0 h-[50%] cursor-default"></div>
|
||||||
<TriangleDownFillIcon
|
<TriangleDownFillIcon
|
||||||
|
|
@ -254,6 +236,25 @@ export function ProbBar(props: { contract: Contract; previewProb?: number }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function quickOutcome(contract: Contract, direction: 'UP' | 'DOWN') {
|
||||||
|
const { outcomeType } = contract
|
||||||
|
|
||||||
|
if (outcomeType === 'BINARY' || outcomeType === 'PSEUDO_NUMERIC') {
|
||||||
|
return direction === 'UP' ? 'YES' : 'NO'
|
||||||
|
}
|
||||||
|
if (outcomeType === 'FREE_RESPONSE') {
|
||||||
|
// TODO: Implement shorting of free response answers
|
||||||
|
if (direction === 'DOWN') {
|
||||||
|
throw new Error("Can't bet against free response answers")
|
||||||
|
}
|
||||||
|
return getTopAnswer(contract)?.id
|
||||||
|
}
|
||||||
|
if (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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function QuickOutcomeView(props: {
|
function QuickOutcomeView(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
previewProb?: number
|
previewProb?: number
|
||||||
|
|
@ -261,9 +262,16 @@ function QuickOutcomeView(props: {
|
||||||
}) {
|
}) {
|
||||||
const { contract, previewProb, caption } = props
|
const { contract, previewProb, caption } = props
|
||||||
const { outcomeType } = contract
|
const { outcomeType } = contract
|
||||||
|
const isPseudoNumeric = outcomeType === 'PSEUDO_NUMERIC'
|
||||||
|
|
||||||
// If there's a preview prob, display that instead of the current prob
|
// If there's a preview prob, display that instead of the current prob
|
||||||
const override =
|
const override =
|
||||||
previewProb === undefined ? undefined : formatPercent(previewProb)
|
previewProb === undefined
|
||||||
|
? undefined
|
||||||
|
: isPseudoNumeric
|
||||||
|
? formatNumericProbability(previewProb, contract)
|
||||||
|
: formatPercent(previewProb)
|
||||||
|
|
||||||
const textColor = `text-${getColor(contract)}`
|
const textColor = `text-${getColor(contract)}`
|
||||||
|
|
||||||
let display: string | undefined
|
let display: string | undefined
|
||||||
|
|
@ -271,6 +279,9 @@ function QuickOutcomeView(props: {
|
||||||
case 'BINARY':
|
case 'BINARY':
|
||||||
display = getBinaryProbPercent(contract)
|
display = getBinaryProbPercent(contract)
|
||||||
break
|
break
|
||||||
|
case 'PSEUDO_NUMERIC':
|
||||||
|
display = formatNumericProbability(getProbability(contract), contract)
|
||||||
|
break
|
||||||
case 'NUMERIC':
|
case 'NUMERIC':
|
||||||
display = formatLargeNumber(getExpectedValue(contract))
|
display = formatLargeNumber(getExpectedValue(contract))
|
||||||
break
|
break
|
||||||
|
|
@ -316,7 +327,8 @@ function getNumericScale(contract: NumericContract) {
|
||||||
export function getColor(contract: Contract) {
|
export function getColor(contract: Contract) {
|
||||||
// TODO: Try injecting a gradient here
|
// TODO: Try injecting a gradient here
|
||||||
// return 'primary'
|
// return 'primary'
|
||||||
const { resolution } = contract
|
const { resolution, outcomeType } = contract
|
||||||
|
|
||||||
if (resolution) {
|
if (resolution) {
|
||||||
return (
|
return (
|
||||||
OUTCOME_TO_COLOR[resolution as resolution] ??
|
OUTCOME_TO_COLOR[resolution as resolution] ??
|
||||||
|
|
@ -325,6 +337,8 @@ export function getColor(contract: Contract) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outcomeType === 'PSEUDO_NUMERIC') return 'blue-400'
|
||||||
|
|
||||||
if ((contract.closeTime ?? Infinity) < Date.now()) {
|
if ((contract.closeTime ?? Infinity) < Date.now()) {
|
||||||
return 'gray-400'
|
return 'gray-400'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import { NumberCancelSelector } from './yes-no-selector'
|
||||||
import { Spacer } from './layout/spacer'
|
import { Spacer } from './layout/spacer'
|
||||||
import { ResolveConfirmationButton } from './confirmation-button'
|
import { ResolveConfirmationButton } from './confirmation-button'
|
||||||
import { resolveMarket } from 'web/lib/firebase/fn-call'
|
import { resolveMarket } from 'web/lib/firebase/fn-call'
|
||||||
import { NumericContract } from 'common/contract'
|
import { NumericContract, PseudoNumericContract } from 'common/contract'
|
||||||
import { BucketInput } from './bucket-input'
|
import { BucketInput } from './bucket-input'
|
||||||
|
|
||||||
export function NumericResolutionPanel(props: {
|
export function NumericResolutionPanel(props: {
|
||||||
creator: User
|
creator: User
|
||||||
contract: NumericContract
|
contract: NumericContract | PseudoNumericContract
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { BinaryContract } from 'common/contract'
|
import { BinaryContract, PseudoNumericContract } from 'common/contract'
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
import { useUserContractBets } from 'web/hooks/use-user-bets'
|
import { useUserContractBets } from 'web/hooks/use-user-bets'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
@ -7,7 +7,7 @@ import clsx from 'clsx'
|
||||||
import { SellSharesModal } from './sell-modal'
|
import { SellSharesModal } from './sell-modal'
|
||||||
|
|
||||||
export function SellButton(props: {
|
export function SellButton(props: {
|
||||||
contract: BinaryContract
|
contract: BinaryContract | PseudoNumericContract
|
||||||
user: User | null | undefined
|
user: User | null | undefined
|
||||||
sharesOutcome: 'YES' | 'NO' | undefined
|
sharesOutcome: 'YES' | 'NO' | undefined
|
||||||
shares: number
|
shares: number
|
||||||
|
|
@ -16,7 +16,8 @@ export function SellButton(props: {
|
||||||
const { contract, user, sharesOutcome, shares, panelClassName } = props
|
const { contract, user, sharesOutcome, shares, panelClassName } = props
|
||||||
const userBets = useUserContractBets(user?.id, contract.id)
|
const userBets = useUserContractBets(user?.id, contract.id)
|
||||||
const [showSellModal, setShowSellModal] = useState(false)
|
const [showSellModal, setShowSellModal] = useState(false)
|
||||||
const { mechanism } = contract
|
const { mechanism, outcomeType } = contract
|
||||||
|
const isPseudoNumeric = outcomeType === 'PSEUDO_NUMERIC'
|
||||||
|
|
||||||
if (sharesOutcome && user && mechanism === 'cpmm-1') {
|
if (sharesOutcome && user && mechanism === 'cpmm-1') {
|
||||||
return (
|
return (
|
||||||
|
|
@ -32,7 +33,10 @@ export function SellButton(props: {
|
||||||
)}
|
)}
|
||||||
onClick={() => setShowSellModal(true)}
|
onClick={() => setShowSellModal(true)}
|
||||||
>
|
>
|
||||||
{'Sell ' + sharesOutcome}
|
Sell{' '}
|
||||||
|
{isPseudoNumeric
|
||||||
|
? { YES: 'HIGH', NO: 'LOW' }[sharesOutcome]
|
||||||
|
: sharesOutcome}
|
||||||
</button>
|
</button>
|
||||||
<div className={'mt-1 w-24 text-center text-sm text-gray-500'}>
|
<div className={'mt-1 w-24 text-center text-sm text-gray-500'}>
|
||||||
{'(' + Math.floor(shares) + ' shares)'}
|
{'(' + Math.floor(shares) + ' shares)'}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { CPMMBinaryContract } from 'common/contract'
|
import { CPMMBinaryContract, PseudoNumericContract } from 'common/contract'
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
import { Modal } from './layout/modal'
|
import { Modal } from './layout/modal'
|
||||||
|
|
@ -11,7 +11,7 @@ import clsx from 'clsx'
|
||||||
|
|
||||||
export function SellSharesModal(props: {
|
export function SellSharesModal(props: {
|
||||||
className?: string
|
className?: string
|
||||||
contract: CPMMBinaryContract
|
contract: CPMMBinaryContract | PseudoNumericContract
|
||||||
userBets: Bet[]
|
userBets: Bet[]
|
||||||
shares: number
|
shares: number
|
||||||
sharesOutcome: 'YES' | 'NO'
|
sharesOutcome: 'YES' | 'NO'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { BinaryContract } from 'common/contract'
|
import { BinaryContract, PseudoNumericContract } from 'common/contract'
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Col } from './layout/col'
|
import { Col } from './layout/col'
|
||||||
|
|
@ -10,7 +10,7 @@ import { useSaveShares } from './use-save-shares'
|
||||||
import { SellSharesModal } from './sell-modal'
|
import { SellSharesModal } from './sell-modal'
|
||||||
|
|
||||||
export function SellRow(props: {
|
export function SellRow(props: {
|
||||||
contract: BinaryContract
|
contract: BinaryContract | PseudoNumericContract
|
||||||
user: User | null | undefined
|
user: User | null | undefined
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
|
|
|
||||||
|
|
@ -142,11 +142,12 @@ export function ContractPageContent(
|
||||||
const { creatorId, isResolved, question, outcomeType } = contract
|
const { creatorId, isResolved, question, outcomeType } = contract
|
||||||
|
|
||||||
const isCreator = user?.id === creatorId
|
const isCreator = user?.id === creatorId
|
||||||
const isBinary = outcomeType === 'BINARY' || outcomeType === 'PSEUDO_NUMERIC'
|
const isBinary = outcomeType === 'BINARY'
|
||||||
|
const isPseudoNumeric = outcomeType === 'PSEUDO_NUMERIC'
|
||||||
const isNumeric = outcomeType === 'NUMERIC'
|
const isNumeric = outcomeType === 'NUMERIC'
|
||||||
const allowTrade = tradingAllowed(contract)
|
const allowTrade = tradingAllowed(contract)
|
||||||
const allowResolve = !isResolved && isCreator && !!user
|
const allowResolve = !isResolved && isCreator && !!user
|
||||||
const hasSidePanel = (isBinary || isNumeric) && (allowTrade || allowResolve)
|
const hasSidePanel = (isBinary || isNumeric || isPseudoNumeric) && (allowTrade || allowResolve)
|
||||||
|
|
||||||
const ogCardProps = getOpenGraphProps(contract)
|
const ogCardProps = getOpenGraphProps(contract)
|
||||||
|
|
||||||
|
|
@ -159,7 +160,7 @@ export function ContractPageContent(
|
||||||
<BetPanel className="hidden xl:flex" contract={contract} />
|
<BetPanel className="hidden xl:flex" contract={contract} />
|
||||||
))}
|
))}
|
||||||
{allowResolve &&
|
{allowResolve &&
|
||||||
(isNumeric ? (
|
(isNumeric || isPseudoNumeric ? (
|
||||||
<NumericResolutionPanel creator={user} contract={contract} />
|
<NumericResolutionPanel creator={user} contract={contract} />
|
||||||
) : (
|
) : (
|
||||||
<ResolutionPanel creator={user} contract={contract} />
|
<ResolutionPanel creator={user} contract={contract} />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user