From 06c5c97a03f60bca637a3beb6058cdaccfdd3fb4 Mon Sep 17 00:00:00 2001 From: mantikoros Date: Thu, 2 Jun 2022 16:25:41 -0500 Subject: [PATCH 1/6] down betting arrow disabled for free response markets --- web/components/contract/quick-bet.tsx | 55 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/web/components/contract/quick-bet.tsx b/web/components/contract/quick-bet.tsx index 8f642656..a9e6f40a 100644 --- a/web/components/contract/quick-bet.tsx +++ b/web/components/contract/quick-bet.tsx @@ -142,32 +142,41 @@ export function QuickBet(props: { contract: Contract; user: User }) { {/* Down bet triangle */} -
-
setDownHover(true)} - onMouseLeave={() => setDownHover(false)} - onClick={() => placeQuickBet('DOWN')} - >
- {hasDownShares > 0 ? ( + {contract.outcomeType !== 'BINARY' ? ( +
+
- ) : ( - - )} -
- {formatMoney(10)}
-
+ ) : ( +
+
setDownHover(true)} + onMouseLeave={() => setDownHover(false)} + onClick={() => placeQuickBet('DOWN')} + >
+ {hasDownShares > 0 ? ( + + ) : ( + + )} +
+ {formatMoney(10)} +
+
+ )} ) } From bbb9a2c1fa40711bf7616f8cfdb6aafb60f89a13 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Thu, 2 Jun 2022 18:22:39 -0500 Subject: [PATCH 2/6] Quick bet: Opposite arrow sells position --- web/components/contract/quick-bet.tsx | 44 +++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/web/components/contract/quick-bet.tsx b/web/components/contract/quick-bet.tsx index a9e6f40a..7dbea978 100644 --- a/web/components/contract/quick-bet.tsx +++ b/web/components/contract/quick-bet.tsx @@ -22,11 +22,14 @@ 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 { sellShares } from 'web/lib/firebase/fn-call' +import { calculateCpmmSale, getCpmmProbability } from 'common/calculate-cpmm' const BET_SIZE = 10 export function QuickBet(props: { contract: Contract; user: User }) { const { contract, user } = props + const isCpmm = contract.mechanism === 'cpmm-1' const userBets = useUserContractBets(user.id, contract.id) const topAnswer = @@ -35,7 +38,7 @@ export function QuickBet(props: { contract: Contract; user: User }) { : undefined // TODO: yes/no from useSaveShares doesn't work on numeric contracts - const { yesFloorShares, noFloorShares } = useSaveShares( + const { yesFloorShares, noFloorShares, yesShares, noShares } = useSaveShares( contract, userBets, topAnswer?.number.toString() || undefined @@ -68,8 +71,38 @@ export function QuickBet(props: { contract: Contract; user: User }) { // Catch any errors from hovering on an invalid option } + let sharesSold: number | undefined + let sellOutcome: 'YES' | 'NO' | undefined + let saleAmount: number | undefined + if (isCpmm && (upHover || downHover)) { + const oppositeShares = upHover ? noShares : yesShares + if (oppositeShares) { + sellOutcome = upHover ? 'NO' : 'YES' + + const prob = getProb(contract) + const maxSharesSold = BET_SIZE / (sellOutcome === 'YES' ? prob : 1 - prob) + sharesSold = Math.min(oppositeShares, maxSharesSold) + + const { newPool, saleValue } = calculateCpmmSale( + contract, + sharesSold, + sellOutcome + ) + saleAmount = saleValue + previewProb = getCpmmProbability(newPool, contract.p) + } + } + async function placeQuickBet(direction: 'UP' | 'DOWN') { const betPromise = async () => { + if (sharesSold && sellOutcome) { + return await sellShares({ + shares: sharesSold, + outcome: sellOutcome, + contractId: contract.id, + }) + } + const outcome = quickOutcome(contract, direction) return await placeBet({ amount: BET_SIZE, @@ -78,9 +111,14 @@ export function QuickBet(props: { contract: Contract; user: User }) { }) } const shortQ = contract.question.slice(0, 20) + const message = + sellOutcome && saleAmount + ? `${formatMoney(saleAmount)} sold of "${shortQ}"...` + : `${formatMoney(BET_SIZE)} on "${shortQ}"...` + toast.promise(betPromise(), { - loading: `${formatMoney(BET_SIZE)} on "${shortQ}"...`, - success: `${formatMoney(BET_SIZE)} on "${shortQ}"...`, + loading: message, + success: message, error: (err) => `${err.message}`, }) } From 0ac289bd44909c4bf041fe8e6b5b1766388fa871 Mon Sep 17 00:00:00 2001 From: Milli Date: Fri, 3 Jun 2022 02:29:54 +0200 Subject: [PATCH 3/6] vscode settings * correct linebreaks * search exclusion * automatic prettifier --- .vscode/settings.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e262c75c..7819cbe0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,13 @@ { "javascript.preferences.importModuleSpecifier": "shortest", - "typescript.preferences.importModuleSpecifier": "shortest" + "typescript.preferences.importModuleSpecifier": "shortest", + "files.eol": "\r\n", + "search.exclude": { + "**/node_modules": true, + "**/package-lock.json": true, + "**/yarn.lock": true + }, + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" } From 0f2a311b74c75aba7b509273de1f30fcd23a072e Mon Sep 17 00:00:00 2001 From: TrueMilli <61841994+TrueMilli@users.noreply.github.com> Date: Fri, 3 Jun 2022 02:30:34 +0200 Subject: [PATCH 4/6] Refactoring (#401) * refactoring (cherry picked from commit 4de86d5b08f9bc1d960b51746260a5b8c5d9b1fd) * removed unused imports and variables * added type for binary resolution * Prettier * const for binary resolutions * using the type "resolution" * Prettier * Update functions/src/create-contract.ts * launch config for debugging with vs code * "Launch Chrome" does not work since login via google is not possible in debugger-chrome * Breakpoints are unbound when attached to chrome --- .vscode/launch.json | 23 +++++++++++++++++++ common/contract.ts | 4 +++- functions/src/resolve-market.ts | 6 ++--- web/components/contract/contract-details.tsx | 6 ----- .../contract/contract-info-dialog.tsx | 3 +-- web/components/contract/quick-bet.tsx | 4 ++-- web/components/outcome-label.tsx | 15 +++++++----- web/components/resolution-panel.tsx | 6 ++--- web/components/yes-no-selector.tsx | 5 ++-- web/hooks/use-measure-size.ts | 4 ++-- 10 files changed, 48 insertions(+), 28 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..c35acf8d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:3000", + "webRoot": "${workspaceRoot}" + }, + { + "type": "chrome", + "request": "attach", + "name": "Attach to Chrome", + "port": 9222, + "urlFilter": "http://localhost:3000/*", + "webRoot": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/common/contract.ts b/common/contract.ts index cf4bae0b..f75cfa4b 100644 --- a/common/contract.ts +++ b/common/contract.ts @@ -71,7 +71,7 @@ export type Binary = { outcomeType: 'BINARY' initialProbability: number resolutionProbability?: number // Used for BINARY markets resolved to MKT - resolution?: 'YES' | 'NO' | 'MKT' | 'CANCEL' + resolution?: resolution } export type FreeResponse = { @@ -91,6 +91,8 @@ export type Numeric = { } export type outcomeType = AnyOutcomeType['outcomeType'] +export type resolution = 'YES' | 'NO' | 'MKT' | 'CANCEL' +export const RESOLUTIONS = ['YES', 'NO', 'MKT', 'CANCEL'] as const export const OUTCOME_TYPES = ['BINARY', 'FREE_RESPONSE', 'NUMERIC'] as const export const MAX_QUESTION_LENGTH = 480 export const MAX_DESCRIPTION_LENGTH = 10000 diff --git a/functions/src/resolve-market.ts b/functions/src/resolve-market.ts index 183a5624..cf8c018f 100644 --- a/functions/src/resolve-market.ts +++ b/functions/src/resolve-market.ts @@ -2,7 +2,7 @@ import * as functions from 'firebase-functions' import * as admin from 'firebase-admin' import { difference, uniq, mapValues, groupBy, sumBy } from 'lodash' -import { Contract } from '../../common/contract' +import { Contract, resolution, RESOLUTIONS } from '../../common/contract' import { User } from '../../common/user' import { Bet } from '../../common/bet' import { getUser, isProd, payUser } from './utils' @@ -21,7 +21,7 @@ export const resolveMarket = functions .https.onCall( async ( data: { - outcome: string + outcome: resolution value?: number contractId: string probabilityInt?: number @@ -42,7 +42,7 @@ export const resolveMarket = functions const { creatorId, outcomeType, closeTime } = contract if (outcomeType === 'BINARY') { - if (!['YES', 'NO', 'MKT', 'CANCEL'].includes(outcome)) + if (!RESOLUTIONS.includes(outcome)) return { status: 'error', message: 'Invalid outcome' } } else if (outcomeType === 'FREE_RESPONSE') { if ( diff --git a/web/components/contract/contract-details.tsx b/web/components/contract/contract-details.tsx index 0115c746..438eb3ec 100644 --- a/web/components/contract/contract-details.tsx +++ b/web/components/contract/contract-details.tsx @@ -1,13 +1,9 @@ -import clsx from 'clsx' import { ClockIcon, DatabaseIcon, PencilIcon, - CurrencyDollarIcon, TrendingUpIcon, - StarIcon, } from '@heroicons/react/outline' -import { StarIcon as SolidStarIcon } from '@heroicons/react/solid' import { Row } from '../layout/row' import { formatMoney } from 'common/util/format' import { UserLink } from '../user-page' @@ -17,7 +13,6 @@ import { contractPool, updateContract, } from 'web/lib/firebase/contracts' -import { Col } from '../layout/col' import dayjs from 'dayjs' import { DateTimeTooltip } from '../datetime-tooltip' import { fromNow } from 'web/lib/util/time' @@ -36,7 +31,6 @@ export function MiscDetails(props: { }) { const { contract, showHotVolume, showCloseTime } = props const { volume, volume24Hours, closeTime, tags } = contract - const { volumeLabel } = contractMetrics(contract) // Show at most one category that this contract is tagged by const categories = CATEGORY_LIST.filter((category) => tags.map((t) => t.toLowerCase()).includes(category) diff --git a/web/components/contract/contract-info-dialog.tsx b/web/components/contract/contract-info-dialog.tsx index 28cd91f4..446d04e7 100644 --- a/web/components/contract/contract-info-dialog.tsx +++ b/web/components/contract/contract-info-dialog.tsx @@ -1,14 +1,13 @@ import { DotsHorizontalIcon } from '@heroicons/react/outline' import clsx from 'clsx' import dayjs from 'dayjs' -import { uniqBy, sum } from 'lodash' +import { uniqBy } from 'lodash' import { useState } from 'react' import { Bet } from 'common/bet' import { Contract } from 'common/contract' import { formatMoney } from 'common/util/format' import { - contractMetrics, contractPath, contractPool, getBinaryProbPercent, diff --git a/web/components/contract/quick-bet.tsx b/web/components/contract/quick-bet.tsx index 7dbea978..1740a6d9 100644 --- a/web/components/contract/quick-bet.tsx +++ b/web/components/contract/quick-bet.tsx @@ -6,7 +6,7 @@ import { } from 'common/calculate' import { getExpectedValue } from 'common/calculate-dpm' import { User } from 'common/user' -import { Contract, NumericContract } from 'common/contract' +import { Contract, NumericContract, resolution } from 'common/contract' import { formatLargeNumber, formatMoney, @@ -311,7 +311,7 @@ export function getColor(contract: Contract, previewProb?: number) { const { resolution } = contract if (resolution) { return ( - OUTCOME_TO_COLOR[resolution as 'YES' | 'NO' | 'CANCEL' | 'MKT'] ?? + OUTCOME_TO_COLOR[resolution as resolution] ?? // If resolved to a FR answer, use 'primary' 'primary' ) diff --git a/web/components/outcome-label.tsx b/web/components/outcome-label.tsx index da8a0e80..6daa855b 100644 --- a/web/components/outcome-label.tsx +++ b/web/components/outcome-label.tsx @@ -3,13 +3,18 @@ import { ReactNode } from 'react' import { Answer } from 'common/answer' import { getProbability } from 'common/calculate' import { getValueFromBucket } from 'common/calculate-dpm' -import { BinaryContract, Contract, FreeResponseContract } from 'common/contract' +import { + BinaryContract, + Contract, + FreeResponseContract, + resolution, +} from 'common/contract' import { formatPercent } from 'common/util/format' import { ClientRender } from './client-render' export function OutcomeLabel(props: { contract: Contract - outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT' | string + outcome: resolution | string truncate: 'short' | 'long' | 'none' value?: number }) { @@ -35,9 +40,7 @@ export function OutcomeLabel(props: { ) } -export function BinaryOutcomeLabel(props: { - outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT' -}) { +export function BinaryOutcomeLabel(props: { outcome: resolution }) { const { outcome } = props if (outcome === 'YES') return @@ -48,7 +51,7 @@ export function BinaryOutcomeLabel(props: { export function BinaryContractOutcomeLabel(props: { contract: BinaryContract - resolution: 'YES' | 'NO' | 'CANCEL' | 'MKT' + resolution: resolution }) { const { contract, resolution } = props diff --git a/web/components/resolution-panel.tsx b/web/components/resolution-panel.tsx index fc031b22..8b453765 100644 --- a/web/components/resolution-panel.tsx +++ b/web/components/resolution-panel.tsx @@ -10,7 +10,7 @@ import { resolveMarket } from 'web/lib/firebase/fn-call' import { ProbabilitySelector } from './probability-selector' import { DPM_CREATOR_FEE } from 'common/fees' import { getProbability } from 'common/calculate' -import { BinaryContract } from 'common/contract' +import { BinaryContract, resolution } from 'common/contract' import { formatMoney } from 'common/util/format' export function ResolutionPanel(props: { @@ -30,9 +30,7 @@ export function ResolutionPanel(props: { ? `${DPM_CREATOR_FEE * 100}% of trader profits` : `${formatMoney(contract.collectedFees.creatorFee)} in fees` - const [outcome, setOutcome] = useState< - 'YES' | 'NO' | 'MKT' | 'CANCEL' | undefined - >() + const [outcome, setOutcome] = useState() const [prob, setProb] = useState(getProbability(contract) * 100) diff --git a/web/components/yes-no-selector.tsx b/web/components/yes-no-selector.tsx index 25bdaaeb..1a5eabf5 100644 --- a/web/components/yes-no-selector.tsx +++ b/web/components/yes-no-selector.tsx @@ -3,6 +3,7 @@ import React, { ReactNode } from 'react' import { formatMoney } from 'common/util/format' import { Col } from './layout/col' import { Row } from './layout/row' +import { resolution } from 'common/contract' export function YesNoSelector(props: { selected?: 'YES' | 'NO' @@ -65,8 +66,8 @@ export function YesNoSelector(props: { } export function YesNoCancelSelector(props: { - selected: 'YES' | 'NO' | 'MKT' | 'CANCEL' | undefined - onSelect: (selected: 'YES' | 'NO' | 'MKT' | 'CANCEL') => void + selected: resolution | undefined + onSelect: (selected: resolution) => void className?: string btnClassName?: string }) { diff --git a/web/hooks/use-measure-size.ts b/web/hooks/use-measure-size.ts index 6959e241..723c2fe4 100644 --- a/web/hooks/use-measure-size.ts +++ b/web/hooks/use-measure-size.ts @@ -16,7 +16,7 @@ export function useListenElemSize( debounceMs: number | undefined = undefined ) { const handleResize = useMemo(() => { - let updateSize = () => { + const updateSize = () => { if (elemRef.current) callback(getSize(elemRef.current)) } @@ -25,7 +25,7 @@ export function useListenElemSize( : updateSize }, [callback, elemRef, debounceMs]) - let elem = elemRef.current + const elem = elemRef.current useLayoutEffect(() => { if (!elemRef.current) return From 226c725e7d4cf80c634aa0d782bebf3d28b9f5e2 Mon Sep 17 00:00:00 2001 From: Milli Date: Fri, 3 Jun 2022 02:29:54 +0200 Subject: [PATCH 5/6] vscode settings * correct linebreaks * search exclusion * automatic prettifier --- .vscode/settings.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e262c75c..7819cbe0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,13 @@ { "javascript.preferences.importModuleSpecifier": "shortest", - "typescript.preferences.importModuleSpecifier": "shortest" + "typescript.preferences.importModuleSpecifier": "shortest", + "files.eol": "\r\n", + "search.exclude": { + "**/node_modules": true, + "**/package-lock.json": true, + "**/yarn.lock": true + }, + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" } From c3b5cc3f7f6093fe358ba5b8838c3258360c49ed Mon Sep 17 00:00:00 2001 From: Milli Date: Fri, 3 Jun 2022 18:41:21 +0200 Subject: [PATCH 6/6] Working debugger config --- .vscode/launch.json | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c35acf8d..d832421b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,23 +1,16 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "chrome", - "request": "launch", - "name": "Launch Chrome against localhost", - "url": "http://localhost:3000", - "webRoot": "${workspaceRoot}" - }, - { - "type": "chrome", - "request": "attach", - "name": "Attach to Chrome", - "port": 9222, - "urlFilter": "http://localhost:3000/*", - "webRoot": "${workspaceFolder}" - } - ] -} \ No newline at end of file + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Chrome", + "type": "chrome", + "request": "attach", + "port": 9222, // chrome needs to be started with the parameter "--remote-debugging-port=9222" + "urlFilter": "http://localhost:3000/*", + "webRoot": "${workspaceFolder}/web" + } + ] +}