bucket input, numeric resolution panel
This commit is contained in:
parent
7cce2cc63f
commit
39b745b25a
|
@ -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(
|
||||
|
|
40
web/components/bucket-input.tsx
Normal file
40
web/components/bucket-input.tsx
Normal 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"
|
||||
/>
|
||||
)
|
||||
}
|
|
@ -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>
|
||||
|
|
107
web/components/numeric-resolution-panel.tsx
Normal file
107
web/components/numeric-resolution-panel.tsx
Normal 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>
|
||||
)
|
||||
}
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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: {
|
||||
|
|
Loading…
Reference in New Issue
Block a user