bucket input, numeric resolution panel

This commit is contained in:
mantikoros 2022-05-12 16:22:17 -04:00
parent 7cce2cc63f
commit 39b745b25a
6 changed files with 166 additions and 140 deletions

View File

@ -67,9 +67,11 @@ export function getNumericBets(
export const getMappedBucket = (value: number, contract: NumericContract) => {
const { bucketCount, min, max } = contract
const cappedValue = Math.min(Math.max(min, value), max)
const mapped = Math.floor((bucketCount * (cappedValue - min)) / (max - min))
return `${mapped}`
const index = Math.floor(((value - min) / (max - min)) * bucketCount)
const bucket = Math.max(Math.min(index, bucketCount - 1), 0)
return `${bucket}`
}
export function getDpmOutcomeProbabilityAfterBet(

View File

@ -0,0 +1,40 @@
import _ from 'lodash'
import { useState } from 'react'
import { NumericContract } from 'common/contract'
import { getMappedBucket } from 'common/calculate-dpm'
import { NumberInput } from './number-input'
export function BucketInput(props: {
contract: NumericContract
isSubmitting?: boolean
onBucketChange: (bucket?: string) => void
}) {
const { contract, isSubmitting, onBucketChange } = props
const [numberString, setNumberString] = useState('')
const onChange = (s: string) => {
setNumberString(s)
const value = parseFloat(s)
const bucket = isFinite(value)
? getMappedBucket(value, contract)
: undefined
onBucketChange(bucket)
}
return (
<NumberInput
inputClassName="w-full max-w-none"
onChange={onChange}
error={undefined}
disabled={isSubmitting}
numberString={numberString}
label="Value"
/>
)
}

View File

@ -15,10 +15,10 @@ import { useUser } from '../hooks/use-user'
import { placeBet } from '../lib/firebase/api-call'
import { firebaseLogin, User } from '../lib/firebase/users'
import { BuyAmountInput } from './amount-input'
import { BucketInput } from './bucket-input'
import { Col } from './layout/col'
import { Row } from './layout/row'
import { Spacer } from './layout/spacer'
import { NumberInput } from './number-input'
export function NumericBetPanel(props: {
contract: NumericContract
@ -51,9 +51,10 @@ function NumericBuyPanel(props: {
onBuySuccess?: () => void
}) {
const { contract, user, onBuySuccess } = props
const { min, max, bucketCount } = contract
const [numberString, setNumberString] = useState('')
const [bucketChoice, setBucketChoice] = useState<string | undefined>(
undefined
)
const [betAmount, setBetAmount] = useState<number | undefined>(undefined)
const [valueError, setValueError] = useState<string | undefined>()
@ -61,19 +62,13 @@ function NumericBuyPanel(props: {
const [isSubmitting, setIsSubmitting] = useState(false)
const [wasSubmitted, setWasSubmitted] = useState(false)
const value = parseFloat(numberString)
const index = Math.floor(((value - min) / (max - min)) * bucketCount)
const bucket = Math.max(Math.min(index, bucketCount - 1), 0)
const bucketChoice = `${bucket}`
console.log('value', value, 'bucket', bucket, 'min', min, 'max', max)
function onBetChange(newAmount: number | undefined) {
setWasSubmitted(false)
setBetAmount(newAmount)
}
async function submitBet() {
if (!user || !betAmount || !isFinite(bucket)) return
if (!user || !betAmount || bucketChoice === undefined) return
setError(undefined)
setIsSubmitting(true)
@ -134,13 +129,11 @@ function NumericBuyPanel(props: {
<div className="my-3 text-left text-sm text-gray-500">
Predicted value
</div>
<NumberInput
inputClassName="w-full max-w-none"
onChange={setNumberString}
error={valueError}
disabled={isSubmitting}
numberString={numberString}
label="Value"
<BucketInput
contract={contract}
isSubmitting={isSubmitting}
onBucketChange={setBucketChoice}
/>
<div className="my-3 text-left text-sm text-gray-500">Bet amount</div>

View File

@ -0,0 +1,107 @@
import clsx from 'clsx'
import React, { useEffect, useState } from 'react'
import { Col } from './layout/col'
import { User } from 'web/lib/firebase/users'
import { NumberCancelSelector } from './yes-no-selector'
import { Spacer } from './layout/spacer'
import { ResolveConfirmationButton } from './confirmation-button'
import { resolveMarket } from 'web/lib/firebase/api-call'
import { NumericContract } from 'common/contract'
import { getMappedBucket } from 'common/calculate-dpm'
import { BucketInput } from './bucket-input'
export function NumericResolutionPanel(props: {
creator: User
contract: NumericContract
className?: string
}) {
useEffect(() => {
// warm up cloud function
resolveMarket({} as any).catch()
}, [])
const { contract, className } = props
const [outcomeMode, setOutcomeMode] = useState<
'NUMBER' | 'CANCEL' | undefined
>()
const [outcome, setOutcome] = useState<string | undefined>()
const [isSubmitting, setIsSubmitting] = useState(false)
const [error, setError] = useState<string | undefined>(undefined)
const resolve = async () => {
if (!outcome) return
let outcomeChoice = outcome
if (outcome !== 'CANCEL') {
const bucket = getMappedBucket(+outcome, contract)
outcomeChoice = `${bucket}`
}
setIsSubmitting(true)
const result = await resolveMarket({
outcome: outcomeChoice,
contractId: contract.id,
}).then((r) => r.data)
console.log('resolved', outcome, 'result:', result)
if (result?.status !== 'success') {
setError(result?.message || 'Error resolving market')
}
setIsSubmitting(false)
}
const submitButtonClass =
outcome === 'CANCEL'
? 'bg-yellow-400 hover:bg-yellow-500'
: outcome
? 'btn-primary'
: 'btn-disabled'
console.log('outcome', outcome)
return (
<Col className={clsx('rounded-md bg-white px-8 py-6', className)}>
<div className="mb-6 whitespace-nowrap text-2xl">Resolve market</div>
<div className="mb-3 text-sm text-gray-500">Outcome</div>
<Spacer h={4} />
<NumberCancelSelector selected={outcomeMode} onSelect={setOutcomeMode} />
<Spacer h={4} />
{outcomeMode === 'NUMBER' && (
<BucketInput
contract={contract}
isSubmitting={isSubmitting}
onBucketChange={setOutcome}
/>
)}
<div>
{outcome === 'CANCEL' ? (
<>All trades will be returned with no fees.</>
) : (
<>Resolving this market will immediately pay out traders.</>
)}
</div>
<Spacer h={4} />
{!!error && <div className="text-red-500">{error}</div>}
<ResolveConfirmationButton
onResolve={resolve}
isSubmitting={isSubmitting}
openModalButtonClass={clsx('w-full mt-2', submitButtonClass)}
submitButtonClass={submitButtonClass}
/>
</Col>
)
}

View File

@ -3,23 +3,15 @@ import React, { useEffect, useState } from 'react'
import { Col } from './layout/col'
import { User } from 'web/lib/firebase/users'
import { NumberCancelSelector, YesNoCancelSelector } from './yes-no-selector'
import { YesNoCancelSelector } from './yes-no-selector'
import { Spacer } from './layout/spacer'
import { ResolveConfirmationButton } from './confirmation-button'
import { resolveMarket } from 'web/lib/firebase/api-call'
import { ProbabilitySelector } from './probability-selector'
import { DPM_CREATOR_FEE } from 'common/fees'
import { getProbability } from 'common/calculate'
import {
Binary,
CPMM,
DPM,
FullContract,
NumericContract,
} from 'common/contract'
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
import { formatMoney } from 'common/util/format'
import { BucketAmountInput } from './amount-input'
import { getMappedBucket } from 'common/calculate-dpm'
export function ResolutionPanel(props: {
creator: User
@ -136,109 +128,3 @@ export function ResolutionPanel(props: {
</Col>
)
}
export function NumericResolutionPanel(props: {
creator: User
contract: NumericContract
className?: string
}) {
useEffect(() => {
// warm up cloud function
resolveMarket({} as any).catch()
}, [])
const { contract, className } = props
const { bucketCount, min, max } = contract
const [outcomeMode, setOutcomeMode] = useState<
'NUMBER' | 'CANCEL' | undefined
>()
const [outcome, setOutcome] = useState<string | undefined>()
const [isSubmitting, setIsSubmitting] = useState(false)
const [valueError, setValueError] = useState<string | undefined>()
const [error, setError] = useState<string | undefined>(undefined)
const resolve = async () => {
if (!outcome) return
let outcomeChoice = outcome
if (outcome !== 'CANCEL') {
const bucket = getMappedBucket(+outcome, contract)
outcomeChoice = `${bucket}`
}
setIsSubmitting(true)
const result = await resolveMarket({
outcome: outcomeChoice,
contractId: contract.id,
}).then((r) => r.data)
console.log('resolved', outcome, 'result:', result)
if (result?.status !== 'success') {
setError(result?.message || 'Error resolving market')
}
setIsSubmitting(false)
}
const submitButtonClass =
outcome === 'CANCEL'
? 'bg-yellow-400 hover:bg-yellow-500'
: outcome
? 'btn-primary'
: 'btn-disabled'
console.log('outcome', outcome)
return (
<Col className={clsx('rounded-md bg-white px-8 py-6', className)}>
<div className="mb-6 whitespace-nowrap text-2xl">Resolve market</div>
<div className="mb-3 text-sm text-gray-500">Outcome</div>
<Spacer h={4} />
<NumberCancelSelector selected={outcomeMode} onSelect={setOutcomeMode} />
<Spacer h={4} />
{outcomeMode === 'NUMBER' && (
<>
<BucketAmountInput
bucket={outcome && outcome !== 'CANCEL' ? +outcome : undefined}
min={min}
max={max}
inputClassName="w-full max-w-none"
onChange={(outcome) =>
setOutcome(outcome ? `${outcome}` : undefined)
}
error={valueError}
setError={setValueError}
disabled={isSubmitting}
/>
</>
)}
<div>
{outcome === 'CANCEL' ? (
<>All trades will be returned with no fees.</>
) : (
<>Resolving this market will immediately pay out traders.</>
)}
</div>
<Spacer h={4} />
{!!error && <div className="text-red-500">{error}</div>}
<ResolveConfirmationButton
onResolve={resolve}
isSubmitting={isSubmitting}
openModalButtonClass={clsx('w-full mt-2', submitButtonClass)}
submitButtonClass={submitButtonClass}
/>
</Col>
)
}

View File

@ -7,10 +7,7 @@ import { ContractOverview } from 'web/components/contract/contract-overview'
import { BetPanel } from 'web/components/bet-panel'
import { Col } from 'web/components/layout/col'
import { useUser } from 'web/hooks/use-user'
import {
NumericResolutionPanel,
ResolutionPanel,
} from 'web/components/resolution-panel'
import { ResolutionPanel } from 'web/components/resolution-panel'
import { Title } from 'web/components/title'
import { Spacer } from 'web/components/layout/spacer'
import { listUsers, User } from 'web/lib/firebase/users'
@ -45,6 +42,7 @@ import { contractTextDetails } from 'web/components/contract/contract-details'
import { useWindowSize } from 'web/hooks/use-window-size'
import Confetti from 'react-confetti'
import { NumericBetPanel } from '../../components/numeric-bet-panel'
import { NumericResolutionPanel } from '../../components/numeric-resolution-panel'
export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz(props: {