diff --git a/common/util/format.ts b/common/util/format.ts index 05a8f702..e4ac0bd7 100644 --- a/common/util/format.ts +++ b/common/util/format.ts @@ -8,14 +8,14 @@ const formatter = new Intl.NumberFormat('en-US', { }) export function formatMoney(amount: number) { - const newAmount = Math.round(amount) === 0 ? 0 : amount // handle -0 case + const newAmount = Math.round(amount) === 0 ? 0 : Math.floor(amount) // handle -0 case return ( ENV_CONFIG.moneyMoniker + ' ' + formatter.format(newAmount).replace('$', '') ) } export function formatWithCommas(amount: number) { - return formatter.format(amount).replace('$', '') + return formatter.format(Math.floor(amount)).replace('$', '') } export function formatPercent(zeroToOne: number) { diff --git a/functions/src/scripts/migrate-to-cfmm.ts b/functions/src/scripts/migrate-to-cfmm.ts new file mode 100644 index 00000000..c9ad8bfa --- /dev/null +++ b/functions/src/scripts/migrate-to-cfmm.ts @@ -0,0 +1,154 @@ +import * as admin from 'firebase-admin' +import * as _ from 'lodash' + +import { initAdmin } from './script-init' +initAdmin('stephenDev') + +import { + Binary, + Contract, + CPMM, + DPM, + FullContract, +} from '../../../common/contract' +import { Bet } from '../../../common/bet' +import { + calculateDpmPayout, + getDpmProbability, +} from '../../../common/calculate-dpm' +import { User } from '../../../common/user' +import { getCpmmInitialLiquidity } from '../../../common/antes' +import { noFees } from '../../../common/fees' +import { addObjects } from '../../../common/util/object' + +type DocRef = admin.firestore.DocumentReference + +const firestore = admin.firestore() + +async function recalculateContract(contractRef: DocRef, isCommit = false) { + await firestore.runTransaction(async (transaction) => { + const contractDoc = await transaction.get(contractRef) + const contract = contractDoc.data() as FullContract + + if (!contract?.slug) { + console.log('missing slug; id=', contractRef.id) + return + } + + console.log('recalculating', contract.slug) + + if ( + contract.mechanism !== 'dpm-2' || + contract.outcomeType !== 'BINARY' || + contract.resolution + ) { + console.log('invalid candidate to port to cfmm') + return + } + + const betsRef = contractRef.collection('bets') + const betDocs = await transaction.get(betsRef) + const bets = _.sortBy( + betDocs.docs.map((d) => d.data() as Bet), + (b) => b.createdTime + ) + + const getSoldBetPayout = (bet: Bet) => { + const soldBet = bets.find((b) => bet.sale?.betId === b.id) + return soldBet + ? -soldBet.amount / Math.sqrt(soldBet.probBefore * soldBet.probAfter) + : 0 + } + + for (let bet of bets) { + const shares = bet.sale + ? getSoldBetPayout(bet) + : bet.isSold + ? bet.amount / Math.sqrt(bet.probBefore * bet.probAfter) // make up fake share qty + : calculateDpmPayout(contract, bet, contract.resolution ?? bet.outcome) + + console.log( + 'converting', + bet.shares, + bet.outcome, + bet.isSold ? '(sold)' : '', + 'shares to', + shares + ) + + if (isCommit) + transaction.update(betsRef.doc(bet.id), { + shares, + dpmShares: bet.shares, + }) + } + + const prob = + contract.resolutionProbability ?? getDpmProbability(contract.totalShares) + + const ante = 100 + const newPool = { YES: ante, NO: ante } + console.log('creating liquidity pool at p=', prob, 'for M$', ante) + + const contractUpdate: Partial = { + pool: newPool, + p: prob, + mechanism: 'cpmm-1', + totalLiquidity: ante, + collectedFees: addObjects(contract.collectedFees ?? noFees, noFees), + } + + const additionalInfo = { + cfmmConversionTime: Date.now(), + dpmPool: contract.pool, + } + + const liquidityDocRef = contractRef.collection('liquidity').doc() + + const lp = getCpmmInitialLiquidity( + { id: 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' } as User, // use @ManifoldMarkets' id + { + ...contract, + ...contractUpdate, + } as FullContract, + liquidityDocRef.id, + ante + ) + + if (isCommit) { + transaction.update(contractRef, { + ...contractUpdate, + ...additionalInfo, + }) + transaction.set(liquidityDocRef, lp) + + console.log('updated', contract.slug) + } + }) +} + +async function main() { + const slug = process.argv[2] + const isCommit = process.argv[3] === 'commit' + + const contractRefs = + slug === 'all' + ? await firestore.collection('contracts').listDocuments() + : await firestore + .collection('contracts') + .where('slug', '==', slug) + .get() + .then((snap) => + !snap.empty ? [firestore.doc(`contracts/${snap.docs[0].id}`)] : [] + ) + + for (let contractRef of contractRefs) { + await recalculateContract(contractRef, isCommit).catch((e) => + console.log('error: ', e, 'id=', contractRef.id) + ) + console.log() + console.log() + } +} + +if (require.main === module) main().then(() => process.exit()) diff --git a/web/components/bets-list.tsx b/web/components/bets-list.tsx index b86910d6..b1059b34 100644 --- a/web/components/bets-list.tsx +++ b/web/components/bets-list.tsx @@ -397,7 +397,10 @@ export function ContractBetsTable(props: { sales.map((sale) => [sale.sale?.betId ?? '', sale]) ) - const [redemptions, normalBets] = _.partition(buys, (b) => b.isRedemption) + const [redemptions, normalBets] = _.partition( + contract.mechanism === 'cpmm-1' ? bets : buys, + (b) => b.isRedemption + ) const amountRedeemed = Math.floor( -0.5 * _.sumBy(redemptions, (b) => b.shares) )