Your outcome/cost=> acceptorOutcome/cost
This commit is contained in:
parent
5cb6f44c9c
commit
4252157290
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.')
|
||||||
|
|
|
@ -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 ?? ''}`
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
|
|
|
@ -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>{' '}*/}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user