Your outcome/cost=> acceptorOutcome/cost

This commit is contained in:
Ian Philips 2022-08-03 08:57:09 -06:00
parent 5cb6f44c9c
commit 4252157290
7 changed files with 58 additions and 94 deletions

View File

@ -19,7 +19,8 @@ export type Challenge = {
creatorOutcome: string creatorOutcome: string
// Different than the creator // Different than the creator
yourOutcome: string acceptorOutcome: string
acceptorAmount: number
// The probability the challenger thinks // The probability the challenger thinks
creatorOutcomeProb: number creatorOutcomeProb: number
@ -51,9 +52,6 @@ export type Acceptance = {
userName: string userName: string
userAvatarUrl: string userAvatarUrl: string
// The amount acceptor put up
amount: number
// The ID of the successful bet that tracks the money moved // The ID of the successful bet that tracks the money moved
betId: string betId: string

View File

@ -50,13 +50,15 @@ export const acceptchallenge = newEndpoint({}, async (req, auth) => {
if (!creatorSnap.exists) throw new APIError(400, 'User not found.') if (!creatorSnap.exists) throw new APIError(400, 'User not found.')
const creator = creatorSnap.data() as User const creator = creatorSnap.data() as User
const { creatorAmount, yourOutcome, creatorOutcome, creatorOutcomeProb } = const {
challenge creatorAmount,
acceptorOutcome,
creatorOutcome,
creatorOutcomeProb,
acceptorAmount,
} = challenge
const yourCost = if (user.balance < acceptorAmount)
((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount
if (user.balance < yourCost)
throw new APIError(400, 'Insufficient balance.') throw new APIError(400, 'Insufficient balance.')
const contract = anyContract as CPMMBinaryContract const contract = anyContract as CPMMBinaryContract
@ -67,21 +69,21 @@ export const acceptchallenge = newEndpoint({}, async (req, auth) => {
'Creating challenge bet for', 'Creating challenge bet for',
user.username, user.username,
shares, shares,
yourOutcome, acceptorOutcome,
'shares', 'shares',
'at', 'at',
formatPercent(creatorOutcomeProb), formatPercent(creatorOutcomeProb),
'for', 'for',
formatMoney(yourCost) formatMoney(acceptorAmount)
) )
const yourNewBet: CandidateBet = removeUndefinedProps({ const yourNewBet: CandidateBet = removeUndefinedProps({
orderAmount: yourCost, orderAmount: acceptorAmount,
amount: yourCost, amount: acceptorAmount,
shares: shares, shares: shares,
isCancelled: false, isCancelled: false,
contractId: contract.id, contractId: contract.id,
outcome: yourOutcome, outcome: acceptorOutcome,
probBefore: creatorOutcomeProb, probBefore: creatorOutcomeProb,
probAfter: creatorOutcomeProb, probAfter: creatorOutcomeProb,
loanAmount: 0, loanAmount: 0,
@ -134,7 +136,7 @@ export const acceptchallenge = newEndpoint({}, async (req, auth) => {
userId: user.id, userId: user.id,
betId: yourNewBetDoc.id, betId: yourNewBetDoc.id,
createdTime, createdTime,
amount: yourCost, amount: acceptorAmount,
userUsername: user.username, userUsername: user.username,
userName: user.name, userName: user.name,
userAvatarUrl: user.avatarUrl, userAvatarUrl: user.avatarUrl,
@ -147,7 +149,7 @@ export const acceptchallenge = newEndpoint({}, async (req, auth) => {
user, user,
creator, creator,
challenge, challenge,
yourCost, acceptorAmount,
contract contract
) )
log('Done, sent notification.') log('Done, sent notification.')

View File

@ -17,7 +17,7 @@ function buildCardUrl(props: OgCardProps, challenge?: Challenge) {
acceptances, acceptances,
creatorOutcomeProb, creatorOutcomeProb,
creatorOutcome, creatorOutcome,
yourOutcome, acceptorOutcome,
} = challenge || {} } = challenge || {}
const { userName, userAvatarUrl } = acceptances?.[0] ?? {} const { userName, userAvatarUrl } = acceptances?.[0] ?? {}
const challengeAmount = const challengeAmount =
@ -35,7 +35,7 @@ function buildCardUrl(props: OgCardProps, challenge?: Challenge) {
const challengeUrlParams = challenge const challengeUrlParams = challenge
? `&creatorAmount=${creatorAmount}&creatorOutcome=${creatorOutcome}` + ? `&creatorAmount=${creatorAmount}&creatorOutcome=${creatorOutcome}` +
`&challengerAmount=${challengeAmount}&challengerOutcome=${yourOutcome}` + `&challengerAmount=${challengeAmount}&challengerOutcome=${acceptorOutcome}` +
`&acceptedName=${userName ?? ''}&acceptedAvatarUrl=${userAvatarUrl ?? ''}` `&acceptedName=${userName ?? ''}&acceptedAvatarUrl=${userAvatarUrl ?? ''}`
: '' : ''

View File

@ -21,9 +21,8 @@ export function AcceptChallengeButton(props: {
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [errorText, setErrorText] = useState('') const [errorText, setErrorText] = useState('')
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const { creatorOutcomeProb, creatorAmount } = challenge const { acceptorAmount } = challenge
const yourCost =
((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount
useEffect(() => { useEffect(() => {
setErrorText('') setErrorText('')
}, [open]) }, [open])
@ -69,7 +68,9 @@ export function AcceptChallengeButton(props: {
<Col className="w-full items-center justify-start gap-2"> <Col className="w-full items-center justify-start gap-2">
<Row className={'w-full justify-start gap-20'}> <Row className={'w-full justify-start gap-20'}>
<span className={'min-w-[4rem] font-bold'}>Cost to you:</span>{' '} <span className={'min-w-[4rem] font-bold'}>Cost to you:</span>{' '}
<span className={'text-red-500'}>{formatMoney(yourCost)}</span> <span className={'text-red-500'}>
{formatMoney(acceptorAmount)}
</span>
</Row> </Row>
{/*<Row className={'w-full justify-start gap-8'}>*/} {/*<Row className={'w-full justify-start gap-8'}>*/}
{/* <span className={'min-w-[4rem] font-bold'}>Probability:</span>{' '}*/} {/* <span className={'min-w-[4rem] font-bold'}>Probability:</span>{' '}*/}

View File

@ -23,7 +23,7 @@ type challengeInfo = {
expiresTime: number | null expiresTime: number | null
message: string message: string
outcome: 'YES' | 'NO' | number outcome: 'YES' | 'NO' | number
prob: number acceptorAmount: number
} }
export function CreateChallengeButton(props: { export function CreateChallengeButton(props: {
user: User | null | undefined user: User | null | undefined
@ -45,10 +45,10 @@ export function CreateChallengeButton(props: {
onCreate={async (newChallenge) => { onCreate={async (newChallenge) => {
const challenge = await createChallenge({ const challenge = await createChallenge({
creator: user, creator: user,
amount: newChallenge.amount, creatorAmount: newChallenge.amount,
expiresTime: newChallenge.expiresTime, expiresTime: newChallenge.expiresTime,
message: newChallenge.message, message: newChallenge.message,
prob: newChallenge.prob / 100, acceptorAmount: newChallenge.acceptorAmount,
outcome: newChallenge.outcome, outcome: newChallenge.outcome,
contract: contract, contract: contract,
}) })
@ -90,7 +90,7 @@ function CreateChallengeForm(props: {
expiresTime: dayjs().add(2, defaultExpire).valueOf(), expiresTime: dayjs().add(2, defaultExpire).valueOf(),
outcome: 'YES', outcome: 'YES',
amount: 100, amount: 100,
prob: prob * 100, acceptorAmount: 100,
message: defaultMessage, message: defaultMessage,
}) })
useEffect(() => { useEffect(() => {
@ -119,7 +119,8 @@ function CreateChallengeForm(props: {
<Title className="!mt-2" text="Challenge a friend to bet " /> <Title className="!mt-2" text="Challenge a friend to bet " />
{/*<Row className="label ">How much?</Row>*/} {/*<Row className="label ">How much?</Row>*/}
<div className="mt-2 flex flex-col flex-wrap gap-x-5 gap-y-2"> <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> <div>You are betting:</div>
<Row className={'form-control w-full justify-start gap-4'}> <Row className={'form-control w-full justify-start gap-4'}>
@ -166,52 +167,6 @@ function CreateChallengeForm(props: {
<span className="bold">{formatMoney(friendCost)}</span> on{' '} <span className="bold">{formatMoney(friendCost)}</span> on{' '}
{challengeInfo.outcome === 'YES' ? <NoLabel /> : <YesLabel />} {challengeInfo.outcome === 'YES' ? <NoLabel /> : <YesLabel />}
</div> </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> </div>
<Row className={'justify-end'}> <Row className={'justify-end'}>
<Button <Button

View File

@ -25,14 +25,21 @@ export function getChallengeUrl(challenge: Challenge) {
export async function createChallenge(data: { export async function createChallenge(data: {
creator: User creator: User
outcome: 'YES' | 'NO' | number outcome: 'YES' | 'NO' | number
prob: number
contract: Contract contract: Contract
amount: number creatorAmount: number
acceptorAmount: number
expiresTime: number | null expiresTime: number | null
message: string message: string
}) { }) {
const { creator, amount, expiresTime, message, prob, contract, outcome } = const {
data 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 // 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/ // See https://zelark.github.io/nano-id-cc/
@ -42,7 +49,10 @@ export async function createChallenge(data: {
) )
const slug = nanoid() 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 = { const challenge: Challenge = {
slug, slug,
@ -50,12 +60,13 @@ export async function createChallenge(data: {
creatorUsername: creator.username, creatorUsername: creator.username,
creatorName: creator.name, creatorName: creator.name,
creatorAvatarUrl: creator.avatarUrl, creatorAvatarUrl: creator.avatarUrl,
creatorAmount: amount, creatorAmount: creatorAmount,
contractSlug: contract.slug, contractSlug: contract.slug,
contractId: contract.id, contractId: contract.id,
creatorOutcome: outcome.toString(), creatorOutcome: outcome.toString(),
yourOutcome: outcome === 'YES' ? 'NO' : 'YES', acceptorOutcome: outcome === 'YES' ? 'NO' : 'YES',
creatorOutcomeProb: prob, creatorOutcomeProb: prob,
acceptorAmount,
createdTime: Date.now(), createdTime: Date.now(),
expiresTime, expiresTime,
maxUses: 1, maxUses: 1,

View File

@ -141,7 +141,8 @@ function ClosedChallengeContent(props: {
creatorAmount, creatorAmount,
creatorOutcome, creatorOutcome,
creatorOutcomeProb, creatorOutcomeProb,
yourOutcome, acceptorOutcome,
acceptorAmount,
} = challenge } = challenge
const user = useUserById(acceptances[0].userId) const user = useUserById(acceptances[0].userId)
@ -155,9 +156,7 @@ function ClosedChallengeContent(props: {
}, [acceptances]) }, [acceptances])
const creatorWon = resolution === creatorOutcome const creatorWon = resolution === creatorOutcome
// const amountWon = creatorWon ? acceptances[0].amount : creatorAmount const amountWon = creatorWon ? acceptorAmount : creatorAmount
const yourCost =
((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount
const href = `https://${DOMAIN}${contractPath(contract)}` const href = `https://${DOMAIN}${contractPath(contract)}`
@ -211,8 +210,8 @@ function ClosedChallengeContent(props: {
<UserBetColumn <UserBetColumn
challenger={user?.id === creator.id ? undefined : user} challenger={user?.id === creator.id ? undefined : user}
outcome={yourOutcome} outcome={acceptorOutcome}
amount={yourCost} amount={acceptorAmount}
isResolved={!!resolution} isResolved={!!resolution}
/> />
</Col> </Col>
@ -240,14 +239,12 @@ function OpenChallengeContent(props: {
creatorAmount, creatorAmount,
creatorId, creatorId,
creatorOutcome, creatorOutcome,
creatorOutcomeProb, acceptorAmount,
yourOutcome, acceptorOutcome,
} = challenge } = challenge
const yourCost =
((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount
const href = `https://${DOMAIN}${contractPath(contract)}` const href = `https://${DOMAIN}${contractPath(contract)}`
const title = `${creator.name} is challenging you to bet` const title = `${creator.name} is challenging you to bet`
return ( return (
@ -276,8 +273,8 @@ function OpenChallengeContent(props: {
<UserBetColumn <UserBetColumn
challenger={user?.id === creatorId ? undefined : user} challenger={user?.id === creatorId ? undefined : user}
outcome={yourOutcome} outcome={acceptorOutcome}
amount={yourCost} amount={acceptorAmount}
/> />
</Col> </Col>