diff --git a/common/bet.ts b/common/bet.ts index 8851e797..d5072c0f 100644 --- a/common/bet.ts +++ b/common/bet.ts @@ -26,11 +26,7 @@ export type Bet = { isAnte?: boolean isLiquidityProvision?: boolean isRedemption?: boolean - // A record of each transaction that partially (or fully) fills the bet amount. - // I.e. A limit order could be filled by partially matching with several bets. - // Non-limit orders can also be filled by matching with multiple limit orders. - fills?: fill[] -} +} & Partial export type NumericBet = Bet & { value: number @@ -39,11 +35,16 @@ export type NumericBet = Bet & { } // Binary market limit order. -export type LimitBet = Bet & { +export type LimitBet = Bet & LimitProps + +type LimitProps = { orderAmount: number // Amount of limit order. limitProb: number // [0, 1]. Bet to this probability. isFilled: boolean // Whether all of the bet amount has been filled. isCancelled: boolean // Whether to prevent any further fills. + // A record of each transaction that partially (or fully) fills the orderAmount. + // I.e. A limit order could be filled by partially matching with several bets. + // Non-limit orders can also be filled by matching with multiple limit orders. fills: fill[] } @@ -53,6 +54,9 @@ export type fill = { amount: number shares: number timestamp: number + // If the fill is a sale, it means the matching bet has shares of the same outcome. + // I.e. -fill.shares === matchedBet.shares + isSale?: boolean } export const MAX_LOAN_PER_CONTRACT = 20 diff --git a/common/calculate-cpmm.ts b/common/calculate-cpmm.ts index 2eaaaa22..ac591f38 100644 --- a/common/calculate-cpmm.ts +++ b/common/calculate-cpmm.ts @@ -1,7 +1,9 @@ import { sum, groupBy, mapValues, sumBy, partition } from 'lodash' +import { LimitBet } from './bet' -import { CREATOR_FEE, Fees, LIQUIDITY_FEE, noFees, PLATFORM_FEE } from './fees' +import { CREATOR_FEE, Fees, LIQUIDITY_FEE, PLATFORM_FEE } from './fees' import { LiquidityProvision } from './liquidity-provision' +import { computeFills } from './new-bet' import { addObjects } from './util/object' export type CpmmState = { @@ -166,119 +168,85 @@ function binarySearch( return mid } -function computeK(y: number, n: number, p: number) { - return y ** p * n ** (1 - p) -} - -function sellSharesK( - y: number, - n: number, - p: number, - s: number, - outcome: 'YES' | 'NO', - b: number -) { - return outcome === 'YES' - ? computeK(y - b + s, n - b, p) - : computeK(y - b, n - b + s, p) -} - -function calculateCpmmShareValue( +function calculateAmountToBuyShares( state: CpmmState, shares: number, - outcome: 'YES' | 'NO' + outcome: 'YES' | 'NO', + unfilledBets: LimitBet[] ) { - const { pool, p } = state + // Search for amount between bounds (0, shares). + // Min share price is M$0, and max is M$1 each. + return binarySearch(0, shares, (amount) => { + const { takers } = computeFills( + outcome, + amount, + state, + undefined, + unfilledBets + ) - // Find bet amount that preserves k after selling shares. - const k = computeK(pool.YES, pool.NO, p) - const otherPool = outcome === 'YES' ? pool.NO : pool.YES - - // Constrain the max sale value to the lessor of 1. shares and 2. the other pool. - // This is because 1. the max value per share is M$ 1, - // and 2. The other pool cannot go negative and the sale value is subtracted from it. - // (Without this, there are multiple solutions for the same k.) - let highAmount = Math.min(shares, otherPool) - let lowAmount = 0 - let mid = 0 - let kGuess = 0 - while (true) { - mid = lowAmount + (highAmount - lowAmount) / 2 - - // Break once we've reached max precision. - if (mid === lowAmount || mid === highAmount) break - - kGuess = sellSharesK(pool.YES, pool.NO, p, shares, outcome, mid) - if (kGuess < k) { - highAmount = mid - } else { - lowAmount = mid - } - } - return mid + const totalShares = sumBy(takers, (taker) => taker.shares) + return totalShares - shares + }) } export function calculateCpmmSale( state: CpmmState, shares: number, - outcome: string + outcome: 'YES' | 'NO', + unfilledBets: LimitBet[] ) { if (Math.round(shares) < 0) { throw new Error('Cannot sell non-positive shares') } - const saleValue = calculateCpmmShareValue( + const oppositeOutcome = outcome === 'YES' ? 'NO' : 'YES' + const buyAmount = calculateAmountToBuyShares( state, shares, - outcome as 'YES' | 'NO' + oppositeOutcome, + unfilledBets ) - const fees = noFees + const { cpmmState, makers, takers, totalFees } = computeFills( + oppositeOutcome, + buyAmount, + state, + undefined, + unfilledBets + ) - // const { fees, remainingBet: saleValue } = getCpmmLiquidityFee( - // contract, - // rawSaleValue, - // outcome === 'YES' ? 'NO' : 'YES' - // ) + // Transform buys of opposite outcome into sells. + const saleTakers = takers.map((taker) => ({ + ...taker, + // You bought opposite shares, which combine with existing shares, removing them. + shares: -taker.shares, + // Opposite shares combine with shares you are selling for M$ of shares. + // You paid taker.amount for the opposite shares. + // Take the negative because this is money you gain. + amount: -(taker.shares - taker.amount), + isSale: true, + })) - const { pool } = state - const { YES: y, NO: n } = pool + const saleValue = -sumBy(saleTakers, (taker) => taker.amount) - const { liquidityFee: fee } = fees - - const [newY, newN] = - outcome === 'YES' - ? [y + shares - saleValue + fee, n - saleValue + fee] - : [y - saleValue + fee, n + shares - saleValue + fee] - - if (newY < 0 || newN < 0) { - console.log('calculateCpmmSale', { - newY, - newN, - y, - n, - shares, - saleValue, - fee, - outcome, - }) - throw new Error('Cannot sell more than in pool') + return { + saleValue, + cpmmState, + fees: totalFees, + makers, + takers: saleTakers, } - - const postBetPool = { YES: newY, NO: newN } - - const { newPool, newP } = addCpmmLiquidity(postBetPool, state.p, fee) - - return { saleValue, newPool, newP, fees } } export function getCpmmProbabilityAfterSale( state: CpmmState, shares: number, - outcome: 'YES' | 'NO' + outcome: 'YES' | 'NO', + unfilledBets: LimitBet[] ) { - const { newPool } = calculateCpmmSale(state, shares, outcome) - return getCpmmProbability(newPool, state.p) + const { cpmmState } = calculateCpmmSale(state, shares, outcome, unfilledBets) + return getCpmmProbability(cpmmState.pool, cpmmState.p) } export function getCpmmLiquidity( diff --git a/common/calculate.ts b/common/calculate.ts index 632634f2..e1f3e239 100644 --- a/common/calculate.ts +++ b/common/calculate.ts @@ -1,5 +1,5 @@ import { maxBy } from 'lodash' -import { Bet } from './bet' +import { Bet, LimitBet } from './bet' import { calculateCpmmSale, getCpmmProbability, @@ -74,11 +74,20 @@ export function calculateShares( : calculateDpmShares(contract.totalShares, bet, betChoice) } -export function calculateSaleAmount(contract: Contract, bet: Bet) { +export function calculateSaleAmount( + contract: Contract, + bet: Bet, + unfilledBets: LimitBet[] +) { return contract.mechanism === 'cpmm-1' && (contract.outcomeType === 'BINARY' || contract.outcomeType === 'PSEUDO_NUMERIC') - ? calculateCpmmSale(contract, Math.abs(bet.shares), bet.outcome).saleValue + ? calculateCpmmSale( + contract, + Math.abs(bet.shares), + bet.outcome as 'YES' | 'NO', + unfilledBets + ).saleValue : calculateDpmSaleAmount(contract, bet) } @@ -91,10 +100,16 @@ export function calculatePayoutAfterCorrectBet(contract: Contract, bet: Bet) { export function getProbabilityAfterSale( contract: Contract, outcome: string, - shares: number + shares: number, + unfilledBets: LimitBet[] ) { return contract.mechanism === 'cpmm-1' - ? getCpmmProbabilityAfterSale(contract, shares, outcome as 'YES' | 'NO') + ? getCpmmProbabilityAfterSale( + contract, + shares, + outcome as 'YES' | 'NO', + unfilledBets + ) : getDpmProbabilityAfterSale(contract.totalShares, outcome, shares) } diff --git a/common/new-bet.ts b/common/new-bet.ts index 719a6533..6025a2f8 100644 --- a/common/new-bet.ts +++ b/common/new-bet.ts @@ -135,10 +135,10 @@ const computeFill = ( return { maker, taker } } -export const getBinaryCpmmBetInfo = ( +export const computeFills = ( outcome: 'YES' | 'NO', betAmount: number, - contract: CPMMBinaryContract | PseudoNumericContract, + state: CpmmState, limitProb: number | undefined, unfilledBets: LimitBet[] ) => { @@ -157,7 +157,7 @@ export const getBinaryCpmmBetInfo = ( }[] = [] let amount = betAmount - let cpmmState = { pool: contract.pool, p: contract.p } + let cpmmState = { pool: state.pool, p: state.p } let totalFees = noFees let i = 0 @@ -185,6 +185,24 @@ export const getBinaryCpmmBetInfo = ( if (floatingEqual(amount, 0)) break } + return { takers, makers, totalFees, cpmmState } +} + +export const getBinaryCpmmBetInfo = ( + outcome: 'YES' | 'NO', + betAmount: number, + contract: CPMMBinaryContract | PseudoNumericContract, + limitProb: number | undefined, + unfilledBets: LimitBet[] +) => { + const { pool, p } = contract + const { takers, makers, cpmmState, totalFees } = computeFills( + outcome, + betAmount, + { pool, p }, + limitProb, + unfilledBets + ) const probBefore = getCpmmProbability(contract.pool, contract.p) const probAfter = getCpmmProbability(cpmmState.pool, cpmmState.p) diff --git a/common/sell-bet.ts b/common/sell-bet.ts index 6d487ff2..e1fd9c5d 100644 --- a/common/sell-bet.ts +++ b/common/sell-bet.ts @@ -1,4 +1,4 @@ -import { Bet } from './bet' +import { Bet, LimitBet } from './bet' import { calculateDpmShareValue, deductDpmFees, @@ -7,6 +7,7 @@ import { import { calculateCpmmSale, getCpmmProbability } from './calculate-cpmm' import { CPMMContract, DPMContract } from './contract' import { DPM_CREATOR_FEE, DPM_PLATFORM_FEE, Fees } from './fees' +import { sumBy } from 'lodash' export type CandidateBet = Omit @@ -78,19 +79,24 @@ export const getCpmmSellBetInfo = ( shares: number, outcome: 'YES' | 'NO', contract: CPMMContract, - prevLoanAmount: number + prevLoanAmount: number, + unfilledBets: LimitBet[] ) => { const { pool, p } = contract - const { saleValue, newPool, newP, fees } = calculateCpmmSale( + const { saleValue, cpmmState, fees, makers, takers } = calculateCpmmSale( contract, shares, - outcome + outcome, + unfilledBets ) const loanPaid = Math.min(prevLoanAmount, saleValue) const probBefore = getCpmmProbability(pool, p) - const probAfter = getCpmmProbability(newPool, p) + const probAfter = getCpmmProbability(cpmmState.pool, cpmmState.p) + + const takerAmount = sumBy(takers, 'amount') + const takerShares = sumBy(takers, 'shares') console.log( 'SELL M$', @@ -104,20 +110,26 @@ export const getCpmmSellBetInfo = ( const newBet: CandidateBet = { contractId: contract.id, - amount: -saleValue, - shares: -shares, + amount: takerAmount, + shares: takerShares, outcome, probBefore, probAfter, createdTime: Date.now(), loanAmount: -loanPaid, fees, + fills: takers, + isFilled: true, + isCancelled: false, + orderAmount: takerAmount, } return { newBet, - newPool, - newP, + newPool: cpmmState.pool, + newP: cpmmState.p, fees, + makers, + takers, } } diff --git a/functions/src/place-bet.ts b/functions/src/place-bet.ts index 68190fc6..d1db82a7 100644 --- a/functions/src/place-bet.ts +++ b/functions/src/place-bet.ts @@ -1,6 +1,11 @@ import * as admin from 'firebase-admin' import { z } from 'zod' -import { FieldValue, Query } from 'firebase-admin/firestore' +import { + DocumentReference, + FieldValue, + Query, + Transaction, +} from 'firebase-admin/firestore' import { groupBy, mapValues, sumBy } from 'lodash' import { APIError, newEndpoint, validate } from './api' @@ -70,12 +75,7 @@ export const placebet = newEndpoint({}, async (req, auth) => { makers, } = await (async (): Promise< BetInfo & { - makers?: { - bet: LimitBet - amount: number - shares: number - timestamp: number - }[] + makers?: maker[] } > => { if ( @@ -83,19 +83,10 @@ export const placebet = newEndpoint({}, async (req, auth) => { mechanism == 'cpmm-1' ) { const { outcome, limitProb } = validate(binarySchema, req.body) - const boundedLimitProb = limitProb ?? (outcome === 'YES' ? 1 : 0) - const unfilledBetsQuery = contractDoc - .collection('bets') - .where('outcome', '==', outcome === 'YES' ? 'NO' : 'YES') - .where('isFilled', '==', false) - .where('isCancelled', '==', false) - .where( - 'limitProb', - outcome === 'YES' ? '<=' : '>=', - boundedLimitProb - ) as Query - const unfilledBetsSnap = await trans.get(unfilledBetsQuery) + const unfilledBetsSnap = await trans.get( + getUnfilledBetsQuery(contractDoc) + ) const unfilledBets = unfilledBetsSnap.docs.map((doc) => doc.data()) return getBinaryCpmmBetInfo( @@ -134,37 +125,7 @@ export const placebet = newEndpoint({}, async (req, auth) => { log('Created new bet document.') if (makers) { - const makersByBet = groupBy(makers, (maker) => maker.bet.id) - for (const makers of Object.values(makersByBet)) { - const bet = makers[0].bet - const newFills = makers.map((maker) => { - const { amount, shares, timestamp } = maker - return { amount, shares, matchedBetId: betDoc.id, timestamp } - }) - const fills = [...bet.fills, ...newFills] - const totalShares = sumBy(fills, 'shares') - const totalAmount = sumBy(fills, 'amount') - const isFilled = floatingEqual(totalAmount, bet.orderAmount) - - log('Updated a matched limit bet.') - trans.update(contractDoc.collection('bets').doc(bet.id), { - fills, - isFilled, - amount: totalAmount, - shares: totalShares, - }) - } - - // Deduct balance of makers. - // TODO: Check if users would go negative from fills and cancel those bets. - const spentByUser = mapValues( - groupBy(makers, (maker) => maker.bet.userId), - (makers) => sumBy(makers, (maker) => maker.amount) - ) - for (const [userId, spent] of Object.entries(spentByUser)) { - const userDoc = firestore.collection('users').doc(userId) - trans.update(userDoc, { balance: FieldValue.increment(-spent) }) - } + updateMakers(makers, betDoc.id, contractDoc, trans) } trans.update(userDoc, { balance: FieldValue.increment(-newBet.amount) }) @@ -193,3 +154,55 @@ export const placebet = newEndpoint({}, async (req, auth) => { }) const firestore = admin.firestore() + +export const getUnfilledBetsQuery = (contractDoc: DocumentReference) => { + return contractDoc + .collection('bets') + .where('isFilled', '==', false) + .where('isCancelled', '==', false) as Query +} + +type maker = { + bet: LimitBet + amount: number + shares: number + timestamp: number +} +export const updateMakers = ( + makers: maker[], + takerBetId: string, + contractDoc: DocumentReference, + trans: Transaction +) => { + const makersByBet = groupBy(makers, (maker) => maker.bet.id) + for (const makers of Object.values(makersByBet)) { + const bet = makers[0].bet + const newFills = makers.map((maker) => { + const { amount, shares, timestamp } = maker + return { amount, shares, matchedBetId: takerBetId, timestamp } + }) + const fills = [...bet.fills, ...newFills] + const totalShares = sumBy(fills, 'shares') + const totalAmount = sumBy(fills, 'amount') + const isFilled = floatingEqual(totalAmount, bet.orderAmount) + + log('Updated a matched limit bet.') + trans.update(contractDoc.collection('bets').doc(bet.id), { + fills, + isFilled, + amount: totalAmount, + shares: totalShares, + }) + } + + // Deduct balance of makers. + // TODO: Check if users would go negative from fills and cancel those bets. + const spentByUser = mapValues( + groupBy(makers, (maker) => maker.bet.userId), + (makers) => sumBy(makers, (maker) => maker.amount) + ) + for (const [userId, spent] of Object.entries(spentByUser)) { + const userDoc = firestore.collection('users').doc(userId) + trans.update(userDoc, { balance: FieldValue.increment(-spent) }) + } +} diff --git a/functions/src/sell-shares.ts b/functions/src/sell-shares.ts index 62e43105..3407760b 100644 --- a/functions/src/sell-shares.ts +++ b/functions/src/sell-shares.ts @@ -9,6 +9,9 @@ import { getCpmmSellBetInfo } from '../../common/sell-bet' import { addObjects, removeUndefinedProps } from '../../common/util/object' import { getValues } from './utils' import { Bet } from '../../common/bet' +import { floatingLesserEqual } from '../../common/util/math' +import { getUnfilledBetsQuery, updateMakers } from './place-bet' +import { FieldValue } from 'firebase-admin/firestore' const bodySchema = z.object({ contractId: z.string(), @@ -46,14 +49,22 @@ export const sellshares = newEndpoint({}, async (req, auth) => { const outcomeBets = userBets.filter((bet) => bet.outcome == outcome) const maxShares = sumBy(outcomeBets, (bet) => bet.shares) - if (shares > maxShares) + if (!floatingLesserEqual(shares, maxShares)) throw new APIError(400, `You can only sell up to ${maxShares} shares.`) - const { newBet, newPool, newP, fees } = getCpmmSellBetInfo( - shares, + const soldShares = Math.min(shares, maxShares) + + const unfilledBetsSnap = await transaction.get( + getUnfilledBetsQuery(contractDoc) + ) + const unfilledBets = unfilledBetsSnap.docs.map((doc) => doc.data()) + + const { newBet, newPool, newP, fees, makers } = getCpmmSellBetInfo( + soldShares, outcome, contract, - prevLoanAmount + prevLoanAmount, + unfilledBets ) if ( @@ -65,11 +76,17 @@ export const sellshares = newEndpoint({}, async (req, auth) => { } const newBetDoc = firestore.collection(`contracts/${contractId}/bets`).doc() - const newBalance = user.balance - newBet.amount + (newBet.loanAmount ?? 0) - const userId = user.id - transaction.update(userDoc, { balance: newBalance }) - transaction.create(newBetDoc, { id: newBetDoc.id, userId, ...newBet }) + updateMakers(makers, newBetDoc.id, contractDoc, transaction) + + transaction.update(userDoc, { + balance: FieldValue.increment(-newBet.amount), + }) + transaction.create(newBetDoc, { + id: newBetDoc.id, + userId: user.id, + ...newBet, + }) transaction.update( contractDoc, removeUndefinedProps({ diff --git a/web/components/bet-panel.tsx b/web/components/bet-panel.tsx index fba8ae03..84ac0584 100644 --- a/web/components/bet-panel.tsx +++ b/web/components/bet-panel.tsx @@ -497,19 +497,21 @@ export function SellPanel(props: { const [isSubmitting, setIsSubmitting] = useState(false) const [wasSubmitted, setWasSubmitted] = useState(false) + const unfilledBets = useUnfilledBets(contract.id) ?? [] + const betDisabled = isSubmitting || !amount || error + // Sell all shares if remaining shares would be < 1 + const sellQuantity = amount === Math.floor(shares) ? shares : amount + async function submitSell() { if (!user || !amount) return setError(undefined) setIsSubmitting(true) - // Sell all shares if remaining shares would be < 1 - const sellAmount = amount === Math.floor(shares) ? shares : amount - await sellShares({ - shares: sellAmount, + shares: sellQuantity, outcome: sharesOutcome, contractId: contract.id, }) @@ -534,18 +536,19 @@ export function SellPanel(props: { outcomeType: contract.outcomeType, slug: contract.slug, contractId: contract.id, - shares: sellAmount, + shares: sellQuantity, outcome: sharesOutcome, }) } const initialProb = getProbability(contract) - const { newPool } = calculateCpmmSale( + const { cpmmState, saleValue } = calculateCpmmSale( contract, - Math.min(amount ?? 0, shares), - sharesOutcome + sellQuantity ?? 0, + sharesOutcome, + unfilledBets ) - const resultProb = getCpmmProbability(newPool, contract.p) + const resultProb = getCpmmProbability(cpmmState.pool, cpmmState.p) const openUserBets = userBets.filter((bet) => !bet.isSold && !bet.sale) const [yesBets, noBets] = partition( @@ -557,17 +560,8 @@ export function SellPanel(props: { sumBy(noBets, (bet) => bet.shares), ] - const sellOutcome = yesShares ? 'YES' : noShares ? 'NO' : undefined const ownedShares = Math.round(yesShares) || Math.round(noShares) - const sharesSold = Math.min(amount ?? 0, ownedShares) - - const { saleValue } = calculateCpmmSale( - contract, - sharesSold, - sellOutcome as 'YES' | 'NO' - ) - const onAmountChange = (amount: number | undefined) => { setAmount(amount) diff --git a/web/components/bets-list.tsx b/web/components/bets-list.tsx index c4dcda88..c7a15a21 100644 --- a/web/components/bets-list.tsx +++ b/web/components/bets-list.tsx @@ -44,6 +44,8 @@ import { NumericContract } from 'common/contract' import { formatNumericProbability } from 'common/pseudo-numeric' import { useUser } from 'web/hooks/use-user' import { SellSharesModal } from './sell-modal' +import { useUnfilledBets } from 'web/hooks/use-bets' +import { LimitBet } from 'common/bet' type BetSort = 'newest' | 'profit' | 'closeTime' | 'value' type BetFilter = 'open' | 'sold' | 'closed' | 'resolved' | 'all' @@ -531,6 +533,8 @@ export function ContractBetsTable(props: { const isNumeric = outcomeType === 'NUMERIC' const isPseudoNumeric = outcomeType === 'PSEUDO_NUMERIC' + const unfilledBets = useUnfilledBets(contract.id) ?? [] + return (
{amountRedeemed > 0 && ( @@ -577,6 +581,7 @@ export function ContractBetsTable(props: { saleBet={salesDict[bet.id]} contract={contract} isYourBet={isYourBets} + unfilledBets={unfilledBets} /> ))} @@ -590,8 +595,9 @@ function BetRow(props: { contract: Contract saleBet?: Bet isYourBet: boolean + unfilledBets: LimitBet[] }) { - const { bet, saleBet, contract, isYourBet } = props + const { bet, saleBet, contract, isYourBet, unfilledBets } = props const { amount, outcome, @@ -621,7 +627,7 @@ function BetRow(props: { formatMoney( isResolved ? resolvedPayout(contract, bet) - : calculateSaleAmount(contract, bet) + : calculateSaleAmount(contract, bet, unfilledBets) ) ) @@ -681,9 +687,16 @@ function SellButton(props: { contract: Contract; bet: Bet }) { outcome === 'NO' ? 'YES' : outcome ) - const outcomeProb = getProbabilityAfterSale(contract, outcome, shares) + const unfilledBets = useUnfilledBets(contract.id) ?? [] - const saleAmount = calculateSaleAmount(contract, bet) + const outcomeProb = getProbabilityAfterSale( + contract, + outcome, + shares, + unfilledBets + ) + + const saleAmount = calculateSaleAmount(contract, bet, unfilledBets) const profit = saleAmount - bet.amount return ( diff --git a/web/components/contract/quick-bet.tsx b/web/components/contract/quick-bet.tsx index adbcc456..9f40fb93 100644 --- a/web/components/contract/quick-bet.tsx +++ b/web/components/contract/quick-bet.tsx @@ -27,6 +27,7 @@ import { sellShares } from 'web/lib/firebase/api-call' import { calculateCpmmSale, getCpmmProbability } from 'common/calculate-cpmm' import { track } from 'web/lib/service/analytics' import { formatNumericProbability } from 'common/pseudo-numeric' +import { useUnfilledBets } from 'web/hooks/use-bets' const BET_SIZE = 10 @@ -36,6 +37,7 @@ export function QuickBet(props: { contract: Contract; user: User }) { const isCpmm = mechanism === 'cpmm-1' const userBets = useUserContractBets(user.id, contract.id) + const unfilledBets = useUnfilledBets(contract.id) ?? [] const topAnswer = outcomeType === 'FREE_RESPONSE' ? getTopAnswer(contract) : undefined @@ -85,13 +87,14 @@ export function QuickBet(props: { contract: Contract; user: User }) { const maxSharesSold = BET_SIZE / (sellOutcome === 'YES' ? prob : 1 - prob) sharesSold = Math.min(oppositeShares, maxSharesSold) - const { newPool, saleValue } = calculateCpmmSale( + const { cpmmState, saleValue } = calculateCpmmSale( contract, sharesSold, - sellOutcome + sellOutcome, + unfilledBets ) saleAmount = saleValue - previewProb = getCpmmProbability(newPool, contract.p) + previewProb = getCpmmProbability(cpmmState.pool, cpmmState.p) } } diff --git a/web/lib/firebase/bets.ts b/web/lib/firebase/bets.ts index 08cb3276..ef0ab55d 100644 --- a/web/lib/firebase/bets.ts +++ b/web/lib/firebase/bets.ts @@ -172,7 +172,6 @@ export function listenForUnfilledBets( ) { const betsQuery = query( collection(db, 'contracts', contractId, 'bets'), - where('contractId', '==', contractId), where('isFilled', '==', false), where('isCancelled', '==', false) )