From 39b745b25a5b5737c9caf098692560abefd7e42c Mon Sep 17 00:00:00 2001 From: mantikoros Date: Thu, 12 May 2022 16:22:17 -0400 Subject: [PATCH] bucket input, numeric resolution panel --- common/calculate-dpm.ts | 8 +- web/components/bucket-input.tsx | 40 +++++++ web/components/numeric-bet-panel.tsx | 27 ++--- web/components/numeric-resolution-panel.tsx | 107 ++++++++++++++++++ web/components/resolution-panel.tsx | 118 +------------------- web/pages/[username]/[contractSlug].tsx | 6 +- 6 files changed, 166 insertions(+), 140 deletions(-) create mode 100644 web/components/bucket-input.tsx create mode 100644 web/components/numeric-resolution-panel.tsx diff --git a/common/calculate-dpm.ts b/common/calculate-dpm.ts index 908af4d2..e6773208 100644 --- a/common/calculate-dpm.ts +++ b/common/calculate-dpm.ts @@ -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( diff --git a/web/components/bucket-input.tsx b/web/components/bucket-input.tsx new file mode 100644 index 00000000..8f101cbd --- /dev/null +++ b/web/components/bucket-input.tsx @@ -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 ( + + ) +} diff --git a/web/components/numeric-bet-panel.tsx b/web/components/numeric-bet-panel.tsx index 8f28c1c8..9c991a1e 100644 --- a/web/components/numeric-bet-panel.tsx +++ b/web/components/numeric-bet-panel.tsx @@ -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( + undefined + ) const [betAmount, setBetAmount] = useState(undefined) const [valueError, setValueError] = useState() @@ -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: {
Predicted value
-
Bet amount
diff --git a/web/components/numeric-resolution-panel.tsx b/web/components/numeric-resolution-panel.tsx new file mode 100644 index 00000000..81c545eb --- /dev/null +++ b/web/components/numeric-resolution-panel.tsx @@ -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() + + const [isSubmitting, setIsSubmitting] = useState(false) + const [error, setError] = useState(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 ( + +
Resolve market
+ +
Outcome
+ + + + + + + + {outcomeMode === 'NUMBER' && ( + + )} + +
+ {outcome === 'CANCEL' ? ( + <>All trades will be returned with no fees. + ) : ( + <>Resolving this market will immediately pay out traders. + )} +
+ + + + {!!error &&
{error}
} + + + + ) +} diff --git a/web/components/resolution-panel.tsx b/web/components/resolution-panel.tsx index 9c82b03c..1aaabcfd 100644 --- a/web/components/resolution-panel.tsx +++ b/web/components/resolution-panel.tsx @@ -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: { ) } - -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() - - const [isSubmitting, setIsSubmitting] = useState(false) - const [valueError, setValueError] = useState() - const [error, setError] = useState(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 ( - -
Resolve market
- -
Outcome
- - - - - - - - {outcomeMode === 'NUMBER' && ( - <> - - setOutcome(outcome ? `${outcome}` : undefined) - } - error={valueError} - setError={setValueError} - disabled={isSubmitting} - /> - - )} - -
- {outcome === 'CANCEL' ? ( - <>All trades will be returned with no fees. - ) : ( - <>Resolving this market will immediately pay out traders. - )} -
- - - - {!!error &&
{error}
} - - - - ) -} diff --git a/web/pages/[username]/[contractSlug].tsx b/web/pages/[username]/[contractSlug].tsx index fb87043c..9e6ed4f3 100644 --- a/web/pages/[username]/[contractSlug].tsx +++ b/web/pages/[username]/[contractSlug].tsx @@ -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: {