From 61a2de5ea2b94e8775969d9c40b49e88c47998cc Mon Sep 17 00:00:00 2001 From: mantikoros Date: Mon, 10 Oct 2022 17:33:08 -0500 Subject: [PATCH] pay back excess liquidity --- common/calculate-cpmm.ts | 45 +++-------- common/payouts-fixed.ts | 13 ++- functions/src/resolve-market.ts | 11 ++- functions/src/withdraw-liquidity.ts | 121 ---------------------------- web/hooks/use-liquidity.ts | 7 +- 5 files changed, 26 insertions(+), 171 deletions(-) delete mode 100644 functions/src/withdraw-liquidity.ts diff --git a/common/calculate-cpmm.ts b/common/calculate-cpmm.ts index 346fca79..ab8aabbe 100644 --- a/common/calculate-cpmm.ts +++ b/common/calculate-cpmm.ts @@ -1,11 +1,10 @@ -import { sum, groupBy, mapValues, sumBy } from 'lodash' +import { groupBy, mapValues, sumBy } from 'lodash' import { LimitBet } from './bet' import { CREATOR_FEE, Fees, LIQUIDITY_FEE, PLATFORM_FEE } from './fees' import { LiquidityProvision } from './liquidity-provision' import { computeFills } from './new-bet' import { binarySearch } from './util/algos' -import { addObjects } from './util/object' export type CpmmState = { pool: { [outcome: string]: number } @@ -267,48 +266,22 @@ export function addCpmmLiquidity( return { newPool, liquidity, newP } } -const calculateLiquidityDelta = (p: number) => (l: LiquidityProvision) => { - const oldLiquidity = getCpmmLiquidity(l.pool, p) +export function getCpmmLiquidityPoolWeights(liquidities: LiquidityProvision[]) { + const userAmounts = groupBy(liquidities, (w) => w.userId) + const totalAmount = sumBy(liquidities, (w) => w.amount) - const newPool = addObjects(l.pool, { YES: l.amount, NO: l.amount }) - const newLiquidity = getCpmmLiquidity(newPool, p) - - const liquidity = newLiquidity - oldLiquidity - return liquidity -} - -export function getCpmmLiquidityPoolWeights( - state: CpmmState, - liquidities: LiquidityProvision[], - excludeAntes: boolean -) { - const calcLiqudity = calculateLiquidityDelta(state.p) - const liquidityShares = liquidities.map(calcLiqudity) - const shareSum = sum(liquidityShares) - - const weights = liquidityShares.map((shares, i) => ({ - weight: shares / shareSum, - providerId: liquidities[i].userId, - })) - - const includedWeights = excludeAntes - ? weights.filter((_, i) => !liquidities[i].isAnte) - : weights - - const userWeights = groupBy(includedWeights, (w) => w.providerId) - const totalUserWeights = mapValues(userWeights, (userWeight) => - sumBy(userWeight, (w) => w.weight) + return mapValues( + userAmounts, + (amounts) => sumBy(amounts, (w) => w.amount) / totalAmount ) - return totalUserWeights } export function getUserLiquidityShares( userId: string, state: CpmmState, - liquidities: LiquidityProvision[], - excludeAntes: boolean + liquidities: LiquidityProvision[] ) { - const weights = getCpmmLiquidityPoolWeights(state, liquidities, excludeAntes) + const weights = getCpmmLiquidityPoolWeights(liquidities) const userWeight = weights[userId] ?? 0 return mapValues(state.pool, (shares) => userWeight * shares) diff --git a/common/payouts-fixed.ts b/common/payouts-fixed.ts index 99e03fac..74e9fe16 100644 --- a/common/payouts-fixed.ts +++ b/common/payouts-fixed.ts @@ -1,4 +1,3 @@ - import { Bet } from './bet' import { getProbability } from './calculate' import { getCpmmLiquidityPoolWeights } from './calculate-cpmm' @@ -56,10 +55,10 @@ export const getLiquidityPoolPayouts = ( outcome: string, liquidities: LiquidityProvision[] ) => { - const { pool } = contract - const finalPool = pool[outcome] + const { pool, subsidyPool } = contract + const finalPool = pool[outcome] + subsidyPool - const weights = getCpmmLiquidityPoolWeights(contract, liquidities, false) + const weights = getCpmmLiquidityPoolWeights(liquidities) return Object.entries(weights).map(([providerId, weight]) => ({ userId: providerId, @@ -95,10 +94,10 @@ export const getLiquidityPoolProbPayouts = ( p: number, liquidities: LiquidityProvision[] ) => { - const { pool } = contract - const finalPool = p * pool.YES + (1 - p) * pool.NO + const { pool, subsidyPool } = contract + const finalPool = p * pool.YES + (1 - p) * pool.NO + subsidyPool - const weights = getCpmmLiquidityPoolWeights(contract, liquidities, false) + const weights = getCpmmLiquidityPoolWeights(liquidities) return Object.entries(weights).map(([providerId, weight]) => ({ userId: providerId, diff --git a/functions/src/resolve-market.ts b/functions/src/resolve-market.ts index ca8f5fc0..4230f0ac 100644 --- a/functions/src/resolve-market.ts +++ b/functions/src/resolve-market.ts @@ -9,7 +9,15 @@ import { RESOLUTIONS, } from '../../common/contract' import { Bet } from '../../common/bet' -import { getContractPath, getUser, getValues, isProd, log, payUser, revalidateStaticProps } from './utils' +import { + getContractPath, + getUser, + getValues, + isProd, + log, + payUser, + revalidateStaticProps, +} from './utils' import { getLoanPayouts, getPayouts, @@ -145,6 +153,7 @@ export const resolvemarket = newEndpoint(opts, async (req, auth) => { resolutions, collectedFees, }), + subsidyPool: 0, } await contractDoc.update(updatedContract) diff --git a/functions/src/withdraw-liquidity.ts b/functions/src/withdraw-liquidity.ts deleted file mode 100644 index 53974f7d..00000000 --- a/functions/src/withdraw-liquidity.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as admin from 'firebase-admin' -import { z } from 'zod' - -import { CPMMContract } from '../../common/contract' -import { User } from '../../common/user' -import { subtractObjects } from '../../common/util/object' -import { LiquidityProvision } from '../../common/liquidity-provision' -import { getUserLiquidityShares } from '../../common/calculate-cpmm' -import { Bet } from '../../common/bet' -import { getProbability } from '../../common/calculate' -import { noFees } from '../../common/fees' - -import { APIError, newEndpoint, validate } from './api' -import { redeemShares } from './redeem-shares' - -const bodySchema = z.object({ - contractId: z.string(), -}) - -export const withdrawliquidity = newEndpoint({}, async (req, auth) => { - const { contractId } = validate(bodySchema, req.body) - - return await firestore - .runTransaction(async (trans) => { - const lpDoc = firestore.doc(`users/${auth.uid}`) - const lpSnap = await trans.get(lpDoc) - if (!lpSnap.exists) throw new APIError(400, 'User not found.') - const lp = lpSnap.data() as User - - const contractDoc = firestore.doc(`contracts/${contractId}`) - const contractSnap = await trans.get(contractDoc) - if (!contractSnap.exists) throw new APIError(400, 'Contract not found.') - const contract = contractSnap.data() as CPMMContract - - const liquidityCollection = firestore.collection( - `contracts/${contractId}/liquidity` - ) - - const liquiditiesSnap = await trans.get(liquidityCollection) - - const liquidities = liquiditiesSnap.docs.map( - (doc) => doc.data() as LiquidityProvision - ) - - const userShares = getUserLiquidityShares( - auth.uid, - contract, - liquidities, - true - ) - - // zero all added amounts for now - // can add support for partial withdrawals in the future - liquiditiesSnap.docs - .filter( - (_, i) => !liquidities[i].isAnte && liquidities[i].userId === auth.uid - ) - .forEach((doc) => trans.update(doc.ref, { amount: 0 })) - - const payout = Math.min(...Object.values(userShares)) - if (payout <= 0) return {} - - const newBalance = lp.balance + payout - const newTotalDeposits = lp.totalDeposits + payout - trans.update(lpDoc, { - balance: newBalance, - totalDeposits: newTotalDeposits, - } as Partial) - - const newPool = subtractObjects(contract.pool, userShares) - - const minPoolShares = Math.min(...Object.values(newPool)) - const adjustedTotal = contract.totalLiquidity - payout - - // total liquidity is a bogus number; use minPoolShares to prevent from going negative - const newTotalLiquidity = Math.max(adjustedTotal, minPoolShares) - - trans.update(contractDoc, { - pool: newPool, - totalLiquidity: newTotalLiquidity, - }) - - const prob = getProbability(contract) - - // surplus shares become user's bets - const bets = Object.entries(userShares) - .map(([outcome, shares]) => - shares - payout < 1 // don't create bet if less than 1 share - ? undefined - : ({ - userId: auth.uid, - contractId: contract.id, - amount: - (outcome === 'YES' ? prob : 1 - prob) * (shares - payout), - shares: shares - payout, - outcome, - probBefore: prob, - probAfter: prob, - createdTime: Date.now(), - isLiquidityProvision: true, - fees: noFees, - } as Omit) - ) - .filter((x) => x !== undefined) - - for (const bet of bets) { - const doc = firestore.collection(`contracts/${contract.id}/bets`).doc() - trans.create(doc, { id: doc.id, ...bet }) - } - - return userShares - }) - .then(async (result) => { - // redeem surplus bet with pre-existing bets - await redeemShares(auth.uid, contractId) - console.log('userid', auth.uid, 'withdraws', result) - return result - }) -}) - -const firestore = admin.firestore() diff --git a/web/hooks/use-liquidity.ts b/web/hooks/use-liquidity.ts index 9c7c2b6f..9c610f3b 100644 --- a/web/hooks/use-liquidity.ts +++ b/web/hooks/use-liquidity.ts @@ -21,11 +21,6 @@ export const useLiquidity = (contractId: string) => { export const useUserLiquidity = (contract: CPMMContract, userId: string) => { const liquidities = useLiquidity(contract.id) - const userShares = getUserLiquidityShares( - userId, - contract, - liquidities ?? [], - true - ) + const userShares = getUserLiquidityShares(userId, contract, liquidities ?? []) return userShares }