diff --git a/common/challenge.ts b/common/challenge.ts index a221a724..344c765f 100644 --- a/common/challenge.ts +++ b/common/challenge.ts @@ -19,7 +19,8 @@ export type Challenge = { creatorOutcome: string // Different than the creator - yourOutcome: string + acceptorOutcome: string + acceptorAmount: number // The probability the challenger thinks creatorOutcomeProb: number @@ -51,9 +52,6 @@ export type Acceptance = { userName: string userAvatarUrl: string - // The amount acceptor put up - amount: number - // The ID of the successful bet that tracks the money moved betId: string diff --git a/functions/src/accept-challenge.ts b/functions/src/accept-challenge.ts index 9b135fe2..8f319887 100644 --- a/functions/src/accept-challenge.ts +++ b/functions/src/accept-challenge.ts @@ -50,13 +50,15 @@ export const acceptchallenge = newEndpoint({}, async (req, auth) => { if (!creatorSnap.exists) throw new APIError(400, 'User not found.') const creator = creatorSnap.data() as User - const { creatorAmount, yourOutcome, creatorOutcome, creatorOutcomeProb } = - challenge + const { + creatorAmount, + acceptorOutcome, + creatorOutcome, + creatorOutcomeProb, + acceptorAmount, + } = challenge - const yourCost = - ((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount - - if (user.balance < yourCost) + if (user.balance < acceptorAmount) throw new APIError(400, 'Insufficient balance.') const contract = anyContract as CPMMBinaryContract @@ -67,21 +69,21 @@ export const acceptchallenge = newEndpoint({}, async (req, auth) => { 'Creating challenge bet for', user.username, shares, - yourOutcome, + acceptorOutcome, 'shares', 'at', formatPercent(creatorOutcomeProb), 'for', - formatMoney(yourCost) + formatMoney(acceptorAmount) ) const yourNewBet: CandidateBet = removeUndefinedProps({ - orderAmount: yourCost, - amount: yourCost, + orderAmount: acceptorAmount, + amount: acceptorAmount, shares: shares, isCancelled: false, contractId: contract.id, - outcome: yourOutcome, + outcome: acceptorOutcome, probBefore: creatorOutcomeProb, probAfter: creatorOutcomeProb, loanAmount: 0, @@ -134,7 +136,7 @@ export const acceptchallenge = newEndpoint({}, async (req, auth) => { userId: user.id, betId: yourNewBetDoc.id, createdTime, - amount: yourCost, + amount: acceptorAmount, userUsername: user.username, userName: user.name, userAvatarUrl: user.avatarUrl, @@ -147,7 +149,7 @@ export const acceptchallenge = newEndpoint({}, async (req, auth) => { user, creator, challenge, - yourCost, + acceptorAmount, contract ) log('Done, sent notification.') diff --git a/web/components/SEO.tsx b/web/components/SEO.tsx index e5ff9360..0d43f9b0 100644 --- a/web/components/SEO.tsx +++ b/web/components/SEO.tsx @@ -17,7 +17,7 @@ function buildCardUrl(props: OgCardProps, challenge?: Challenge) { acceptances, creatorOutcomeProb, creatorOutcome, - yourOutcome, + acceptorOutcome, } = challenge || {} const { userName, userAvatarUrl } = acceptances?.[0] ?? {} const challengeAmount = @@ -35,7 +35,7 @@ function buildCardUrl(props: OgCardProps, challenge?: Challenge) { const challengeUrlParams = challenge ? `&creatorAmount=${creatorAmount}&creatorOutcome=${creatorOutcome}` + - `&challengerAmount=${challengeAmount}&challengerOutcome=${yourOutcome}` + + `&challengerAmount=${challengeAmount}&challengerOutcome=${acceptorOutcome}` + `&acceptedName=${userName ?? ''}&acceptedAvatarUrl=${userAvatarUrl ?? ''}` : '' diff --git a/web/components/challenges/accept-challenge-button.tsx b/web/components/challenges/accept-challenge-button.tsx index 46a769c2..86eb4079 100644 --- a/web/components/challenges/accept-challenge-button.tsx +++ b/web/components/challenges/accept-challenge-button.tsx @@ -21,9 +21,8 @@ export function AcceptChallengeButton(props: { const [open, setOpen] = useState(false) const [errorText, setErrorText] = useState('') const [loading, setLoading] = useState(false) - const { creatorOutcomeProb, creatorAmount } = challenge - const yourCost = - ((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount + const { acceptorAmount } = challenge + useEffect(() => { setErrorText('') }, [open]) @@ -69,7 +68,9 @@ export function AcceptChallengeButton(props: { Cost to you:{' '} - {formatMoney(yourCost)} + + {formatMoney(acceptorAmount)} + {/**/} {/* Probability:{' '}*/} diff --git a/web/components/challenges/create-challenge-button.tsx b/web/components/challenges/create-challenge-button.tsx index 7276a9ee..cc03c0ba 100644 --- a/web/components/challenges/create-challenge-button.tsx +++ b/web/components/challenges/create-challenge-button.tsx @@ -23,7 +23,7 @@ type challengeInfo = { expiresTime: number | null message: string outcome: 'YES' | 'NO' | number - prob: number + acceptorAmount: number } export function CreateChallengeButton(props: { user: User | null | undefined @@ -45,10 +45,10 @@ export function CreateChallengeButton(props: { onCreate={async (newChallenge) => { const challenge = await createChallenge({ creator: user, - amount: newChallenge.amount, + creatorAmount: newChallenge.amount, expiresTime: newChallenge.expiresTime, message: newChallenge.message, - prob: newChallenge.prob / 100, + acceptorAmount: newChallenge.acceptorAmount, outcome: newChallenge.outcome, contract: contract, }) @@ -90,7 +90,7 @@ function CreateChallengeForm(props: { expiresTime: dayjs().add(2, defaultExpire).valueOf(), outcome: 'YES', amount: 100, - prob: prob * 100, + acceptorAmount: 100, message: defaultMessage, }) useEffect(() => { @@ -119,7 +119,8 @@ function CreateChallengeForm(props: { {/*<Row className="label ">How much?</Row>*/} <div className="mt-2 flex flex-col flex-wrap gap-x-5 gap-y-2"> - <div className="mb-4 italic">{contract.question}</div> + {/*<div>Question:</div>*/} + {/*<div className="mb-4 italic">{contract.question}</div>*/} <div>You are betting:</div> <Row className={'form-control w-full justify-start gap-4'}> @@ -166,52 +167,6 @@ function CreateChallengeForm(props: { <span className="bold">{formatMoney(friendCost)}</span> on{' '} {challengeInfo.outcome === 'YES' ? <NoLabel /> : <YesLabel />} </div> - {/*<div className="form-control flex flex-row gap-8">*/} - {/* /!*<Col className={'mt-9 justify-center'}>at</Col>*!/*/} - {/* <Col>*/} - {/* <label className="label ">At</label>*/} - {/* <div className="relative">*/} - {/* <input*/} - {/* className="input input-bordered max-w-[5rem]"*/} - {/* type="number"*/} - {/* min={1}*/} - {/* max={100}*/} - {/* value={challengeInfo.prob}*/} - {/* onChange={(e) =>*/} - {/* setChallengeInfo((m: challengeInfo) => {*/} - {/* return {*/} - {/* ...m,*/} - {/* prob: parseFloat(e.target.value),*/} - {/* }*/} - {/* })*/} - {/* }*/} - {/* />*/} - {/* <span className="absolute top-3.5 -right-5 text-sm text-gray-600">*/} - {/* %*/} - {/* </span>*/} - {/* </div>*/} - {/* </Col>*/} - {/*</div>*/} - - {/*<div className="form-control w-full">*/} - {/* <label className="label">Message</label>*/} - {/* <Textarea*/} - {/* placeholder={defaultMessage}*/} - {/* className="input input-bordered resize-none"*/} - {/* autoFocus*/} - {/* value={*/} - {/* challengeInfo.message !== defaultMessage*/} - {/* ? challengeInfo.message*/} - {/* : ''*/} - {/* }*/} - {/* rows={2}*/} - {/* onChange={(e) =>*/} - {/* setChallengeInfo((m: challengeInfo) => {*/} - {/* return { ...m, message: e.target.value }*/} - {/* })*/} - {/* }*/} - {/* />*/} - {/*</div>*/} </div> <Row className={'justify-end'}> <Button diff --git a/web/lib/firebase/challenges.ts b/web/lib/firebase/challenges.ts index 63b06409..81b784d6 100644 --- a/web/lib/firebase/challenges.ts +++ b/web/lib/firebase/challenges.ts @@ -25,14 +25,21 @@ export function getChallengeUrl(challenge: Challenge) { export async function createChallenge(data: { creator: User outcome: 'YES' | 'NO' | number - prob: number contract: Contract - amount: number + creatorAmount: number + acceptorAmount: number expiresTime: number | null message: string }) { - const { creator, amount, expiresTime, message, prob, contract, outcome } = - data + const { + creator, + creatorAmount, + expiresTime, + message, + contract, + outcome, + acceptorAmount, + } = data // At 100 IDs per hour, using this alphabet and 8 chars, there's a 1% chance of collision in 2 years // See https://zelark.github.io/nano-id-cc/ @@ -42,7 +49,10 @@ export async function createChallenge(data: { ) const slug = nanoid() - if (amount <= 0 || isNaN(amount) || !isFinite(amount)) return null + if (creatorAmount <= 0 || isNaN(creatorAmount) || !isFinite(creatorAmount)) + return null + + const prob = 1 / (acceptorAmount / creatorAmount + 1) const challenge: Challenge = { slug, @@ -50,12 +60,13 @@ export async function createChallenge(data: { creatorUsername: creator.username, creatorName: creator.name, creatorAvatarUrl: creator.avatarUrl, - creatorAmount: amount, + creatorAmount: creatorAmount, contractSlug: contract.slug, contractId: contract.id, creatorOutcome: outcome.toString(), - yourOutcome: outcome === 'YES' ? 'NO' : 'YES', + acceptorOutcome: outcome === 'YES' ? 'NO' : 'YES', creatorOutcomeProb: prob, + acceptorAmount, createdTime: Date.now(), expiresTime, maxUses: 1, diff --git a/web/pages/challenges/[username]/[contractSlug]/[challengeSlug].tsx b/web/pages/challenges/[username]/[contractSlug]/[challengeSlug].tsx index 85b80c76..af934cc8 100644 --- a/web/pages/challenges/[username]/[contractSlug]/[challengeSlug].tsx +++ b/web/pages/challenges/[username]/[contractSlug]/[challengeSlug].tsx @@ -141,7 +141,8 @@ function ClosedChallengeContent(props: { creatorAmount, creatorOutcome, creatorOutcomeProb, - yourOutcome, + acceptorOutcome, + acceptorAmount, } = challenge const user = useUserById(acceptances[0].userId) @@ -155,9 +156,7 @@ function ClosedChallengeContent(props: { }, [acceptances]) const creatorWon = resolution === creatorOutcome - // const amountWon = creatorWon ? acceptances[0].amount : creatorAmount - const yourCost = - ((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount + const amountWon = creatorWon ? acceptorAmount : creatorAmount const href = `https://${DOMAIN}${contractPath(contract)}` @@ -211,8 +210,8 @@ function ClosedChallengeContent(props: { <UserBetColumn challenger={user?.id === creator.id ? undefined : user} - outcome={yourOutcome} - amount={yourCost} + outcome={acceptorOutcome} + amount={acceptorAmount} isResolved={!!resolution} /> </Col> @@ -240,14 +239,12 @@ function OpenChallengeContent(props: { creatorAmount, creatorId, creatorOutcome, - creatorOutcomeProb, - yourOutcome, + acceptorAmount, + acceptorOutcome, } = challenge - const yourCost = - ((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount - const href = `https://${DOMAIN}${contractPath(contract)}` + const title = `${creator.name} is challenging you to bet` return ( @@ -276,8 +273,8 @@ function OpenChallengeContent(props: { <UserBetColumn challenger={user?.id === creatorId ? undefined : user} - outcome={yourOutcome} - amount={yourCost} + outcome={acceptorOutcome} + amount={acceptorAmount} /> </Col>