This commit is contained in:
		
						commit
						dc7e47cb2b
					
				|  | @ -1,4 +1,4 @@ | ||||||
| import { addCpmmLiquidity, getCpmmLiquidity } from './calculate-cpmm' | import { getCpmmLiquidity } from './calculate-cpmm' | ||||||
| import { CPMMContract } from './contract' | import { CPMMContract } from './contract' | ||||||
| import { LiquidityProvision } from './liquidity-provision' | import { LiquidityProvision } from './liquidity-provision' | ||||||
| 
 | 
 | ||||||
|  | @ -8,25 +8,23 @@ export const getNewLiquidityProvision = ( | ||||||
|   contract: CPMMContract, |   contract: CPMMContract, | ||||||
|   newLiquidityProvisionId: string |   newLiquidityProvisionId: string | ||||||
| ) => { | ) => { | ||||||
|   const { pool, p, totalLiquidity } = contract |   const { pool, p, totalLiquidity, subsidyPool } = contract | ||||||
| 
 | 
 | ||||||
|   const { newPool, newP } = addCpmmLiquidity(pool, p, amount) |   const liquidity = getCpmmLiquidity(pool, p) | ||||||
| 
 |  | ||||||
|   const liquidity = |  | ||||||
|     getCpmmLiquidity(newPool, newP) - getCpmmLiquidity(pool, newP) |  | ||||||
| 
 | 
 | ||||||
|   const newLiquidityProvision: LiquidityProvision = { |   const newLiquidityProvision: LiquidityProvision = { | ||||||
|     id: newLiquidityProvisionId, |     id: newLiquidityProvisionId, | ||||||
|     userId: userId, |     userId: userId, | ||||||
|     contractId: contract.id, |     contractId: contract.id, | ||||||
|     amount, |     amount, | ||||||
|     pool: newPool, |     pool, | ||||||
|     p: newP, |     p, | ||||||
|     liquidity, |     liquidity, | ||||||
|     createdTime: Date.now(), |     createdTime: Date.now(), | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const newTotalLiquidity = (totalLiquidity ?? 0) + amount |   const newTotalLiquidity = (totalLiquidity ?? 0) + amount | ||||||
|  |   const newSubsidyPool = (subsidyPool ?? 0) + amount | ||||||
| 
 | 
 | ||||||
|   return { newLiquidityProvision, newPool, newP, newTotalLiquidity } |   return { newLiquidityProvision, newTotalLiquidity, newSubsidyPool } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,11 +1,10 @@ | ||||||
| import { sum, groupBy, mapValues, sumBy } from 'lodash' | import { groupBy, mapValues, sumBy } from 'lodash' | ||||||
| import { LimitBet } from './bet' | import { LimitBet } from './bet' | ||||||
| 
 | 
 | ||||||
| import { CREATOR_FEE, Fees, LIQUIDITY_FEE, PLATFORM_FEE } from './fees' | import { CREATOR_FEE, Fees, LIQUIDITY_FEE, PLATFORM_FEE } from './fees' | ||||||
| import { LiquidityProvision } from './liquidity-provision' | import { LiquidityProvision } from './liquidity-provision' | ||||||
| import { computeFills } from './new-bet' | import { computeFills } from './new-bet' | ||||||
| import { binarySearch } from './util/algos' | import { binarySearch } from './util/algos' | ||||||
| import { addObjects } from './util/object' |  | ||||||
| 
 | 
 | ||||||
| export type CpmmState = { | export type CpmmState = { | ||||||
|   pool: { [outcome: string]: number } |   pool: { [outcome: string]: number } | ||||||
|  | @ -267,48 +266,22 @@ export function addCpmmLiquidity( | ||||||
|   return { newPool, liquidity, newP } |   return { newPool, liquidity, newP } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const calculateLiquidityDelta = (p: number) => (l: LiquidityProvision) => { | export function getCpmmLiquidityPoolWeights(liquidities: LiquidityProvision[]) { | ||||||
|   const oldLiquidity = getCpmmLiquidity(l.pool, p) |   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 }) |   return mapValues( | ||||||
|   const newLiquidity = getCpmmLiquidity(newPool, p) |     userAmounts, | ||||||
| 
 |     (amounts) => sumBy(amounts, (w) => w.amount) / totalAmount | ||||||
|   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 totalUserWeights |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function getUserLiquidityShares( | export function getUserLiquidityShares( | ||||||
|   userId: string, |   userId: string, | ||||||
|   state: CpmmState, |   state: CpmmState, | ||||||
|   liquidities: LiquidityProvision[], |   liquidities: LiquidityProvision[] | ||||||
|   excludeAntes: boolean |  | ||||||
| ) { | ) { | ||||||
|   const weights = getCpmmLiquidityPoolWeights(state, liquidities, excludeAntes) |   const weights = getCpmmLiquidityPoolWeights(liquidities) | ||||||
|   const userWeight = weights[userId] ?? 0 |   const userWeight = weights[userId] ?? 0 | ||||||
| 
 | 
 | ||||||
|   return mapValues(state.pool, (shares) => userWeight * shares) |   return mapValues(state.pool, (shares) => userWeight * shares) | ||||||
|  |  | ||||||
|  | @ -91,7 +91,8 @@ export type CPMM = { | ||||||
|   mechanism: 'cpmm-1' |   mechanism: 'cpmm-1' | ||||||
|   pool: { [outcome: string]: number } |   pool: { [outcome: string]: number } | ||||||
|   p: number // probability constant in y^p * n^(1-p) = k
 |   p: number // probability constant in y^p * n^(1-p) = k
 | ||||||
|   totalLiquidity: number // in M$
 |   totalLiquidity: number // for historical reasons, this the total subsidy amount added in M$
 | ||||||
|  |   subsidyPool: number // current value of subsidy pool in M$
 | ||||||
|   prob: number |   prob: number | ||||||
|   probChanges: { |   probChanges: { | ||||||
|     day: number |     day: number | ||||||
|  |  | ||||||
|  | @ -16,3 +16,5 @@ export const BETTING_STREAK_BONUS_MAX = econ?.BETTING_STREAK_BONUS_MAX ?? 25 | ||||||
| export const BETTING_STREAK_RESET_HOUR = econ?.BETTING_STREAK_RESET_HOUR ?? 7 | export const BETTING_STREAK_RESET_HOUR = econ?.BETTING_STREAK_RESET_HOUR ?? 7 | ||||||
| export const FREE_MARKETS_PER_USER_MAX = econ?.FREE_MARKETS_PER_USER_MAX ?? 5 | export const FREE_MARKETS_PER_USER_MAX = econ?.FREE_MARKETS_PER_USER_MAX ?? 5 | ||||||
| export const COMMENT_BOUNTY_AMOUNT = econ?.COMMENT_BOUNTY_AMOUNT ?? 250 | export const COMMENT_BOUNTY_AMOUNT = econ?.COMMENT_BOUNTY_AMOUNT ?? 250 | ||||||
|  | 
 | ||||||
|  | export const UNIQUE_BETTOR_LIQUIDITY = 20 | ||||||
|  |  | ||||||
|  | @ -112,6 +112,7 @@ const getBinaryCpmmProps = (initialProb: number, ante: number) => { | ||||||
|     mechanism: 'cpmm-1', |     mechanism: 'cpmm-1', | ||||||
|     outcomeType: 'BINARY', |     outcomeType: 'BINARY', | ||||||
|     totalLiquidity: ante, |     totalLiquidity: ante, | ||||||
|  |     subsidyPool: 0, | ||||||
|     initialProbability: p, |     initialProbability: p, | ||||||
|     p, |     p, | ||||||
|     pool: pool, |     pool: pool, | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| 
 |  | ||||||
| import { Bet } from './bet' | import { Bet } from './bet' | ||||||
| import { getProbability } from './calculate' | import { getProbability } from './calculate' | ||||||
| import { getCpmmLiquidityPoolWeights } from './calculate-cpmm' | import { getCpmmLiquidityPoolWeights } from './calculate-cpmm' | ||||||
|  | @ -56,10 +55,10 @@ export const getLiquidityPoolPayouts = ( | ||||||
|   outcome: string, |   outcome: string, | ||||||
|   liquidities: LiquidityProvision[] |   liquidities: LiquidityProvision[] | ||||||
| ) => { | ) => { | ||||||
|   const { pool } = contract |   const { pool, subsidyPool } = contract | ||||||
|   const finalPool = pool[outcome] |   const finalPool = pool[outcome] + subsidyPool | ||||||
| 
 | 
 | ||||||
|   const weights = getCpmmLiquidityPoolWeights(contract, liquidities, false) |   const weights = getCpmmLiquidityPoolWeights(liquidities) | ||||||
| 
 | 
 | ||||||
|   return Object.entries(weights).map(([providerId, weight]) => ({ |   return Object.entries(weights).map(([providerId, weight]) => ({ | ||||||
|     userId: providerId, |     userId: providerId, | ||||||
|  | @ -95,10 +94,10 @@ export const getLiquidityPoolProbPayouts = ( | ||||||
|   p: number, |   p: number, | ||||||
|   liquidities: LiquidityProvision[] |   liquidities: LiquidityProvision[] | ||||||
| ) => { | ) => { | ||||||
|   const { pool } = contract |   const { pool, subsidyPool } = contract | ||||||
|   const finalPool = p * pool.YES + (1 - p) * pool.NO |   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]) => ({ |   return Object.entries(weights).map(([providerId, weight]) => ({ | ||||||
|     userId: providerId, |     userId: providerId, | ||||||
|  |  | ||||||
|  | @ -60,6 +60,16 @@ export function formatLargeNumber(num: number, sigfigs = 2): string { | ||||||
|   return `${numStr}${suffix[i] ?? ''}` |   return `${numStr}${suffix[i] ?? ''}` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function shortFormatNumber(num: number): string { | ||||||
|  |   if (num < 1000) return showPrecision(num, 3) | ||||||
|  | 
 | ||||||
|  |   const suffix = ['', 'K', 'M', 'B', 'T', 'Q'] | ||||||
|  |   const i = Math.floor(Math.log10(num) / 3) | ||||||
|  | 
 | ||||||
|  |   const numStr = showPrecision(num / Math.pow(10, 3 * i), 2) | ||||||
|  |   return `${numStr}${suffix[i] ?? ''}` | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export function toCamelCase(words: string) { | export function toCamelCase(words: string) { | ||||||
|   const camelCase = words |   const camelCase = words | ||||||
|     .split(' ') |     .split(' ') | ||||||
|  |  | ||||||
|  | @ -3,24 +3,18 @@ import { z } from 'zod' | ||||||
| 
 | 
 | ||||||
| import { Contract, CPMMContract } from '../../common/contract' | import { Contract, CPMMContract } from '../../common/contract' | ||||||
| import { User } from '../../common/user' | import { User } from '../../common/user' | ||||||
| import { removeUndefinedProps } from '../../common/util/object' |  | ||||||
| import { getNewLiquidityProvision } from '../../common/add-liquidity' | import { getNewLiquidityProvision } from '../../common/add-liquidity' | ||||||
| import { APIError, newEndpoint, validate } from './api' | import { APIError, newEndpoint, validate } from './api' | ||||||
| import { |  | ||||||
|   DEV_HOUSE_LIQUIDITY_PROVIDER_ID, |  | ||||||
|   HOUSE_LIQUIDITY_PROVIDER_ID, |  | ||||||
| } from '../../common/antes' |  | ||||||
| import { isProd } from './utils' |  | ||||||
| 
 | 
 | ||||||
| const bodySchema = z.object({ | const bodySchema = z.object({ | ||||||
|   contractId: z.string(), |   contractId: z.string(), | ||||||
|   amount: z.number().gt(0), |   amount: z.number().gt(0), | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| export const addliquidity = newEndpoint({}, async (req, auth) => { | export const addsubsidy = newEndpoint({}, async (req, auth) => { | ||||||
|   const { amount, contractId } = validate(bodySchema, req.body) |   const { amount, contractId } = validate(bodySchema, req.body) | ||||||
| 
 | 
 | ||||||
|   if (!isFinite(amount)) throw new APIError(400, 'Invalid amount') |   if (!isFinite(amount) || amount < 1) throw new APIError(400, 'Invalid amount') | ||||||
| 
 | 
 | ||||||
|   // run as transaction to prevent race conditions
 |   // run as transaction to prevent race conditions
 | ||||||
|   return await firestore.runTransaction(async (transaction) => { |   return await firestore.runTransaction(async (transaction) => { | ||||||
|  | @ -50,7 +44,7 @@ export const addliquidity = newEndpoint({}, async (req, auth) => { | ||||||
|       .collection(`contracts/${contractId}/liquidity`) |       .collection(`contracts/${contractId}/liquidity`) | ||||||
|       .doc() |       .doc() | ||||||
| 
 | 
 | ||||||
|     const { newLiquidityProvision, newPool, newP, newTotalLiquidity } = |     const { newLiquidityProvision, newTotalLiquidity, newSubsidyPool } = | ||||||
|       getNewLiquidityProvision( |       getNewLiquidityProvision( | ||||||
|         user.id, |         user.id, | ||||||
|         amount, |         amount, | ||||||
|  | @ -58,21 +52,10 @@ export const addliquidity = newEndpoint({}, async (req, auth) => { | ||||||
|         newLiquidityProvisionDoc.id |         newLiquidityProvisionDoc.id | ||||||
|       ) |       ) | ||||||
| 
 | 
 | ||||||
|     if (newP !== undefined && !isFinite(newP)) { |     transaction.update(contractDoc, { | ||||||
|       return { |       subsidyPool: newSubsidyPool, | ||||||
|         status: 'error', |  | ||||||
|         message: 'Liquidity injection rejected due to overflow error.', |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     transaction.update( |  | ||||||
|       contractDoc, |  | ||||||
|       removeUndefinedProps({ |  | ||||||
|         pool: newPool, |  | ||||||
|         p: newP, |  | ||||||
|       totalLiquidity: newTotalLiquidity, |       totalLiquidity: newTotalLiquidity, | ||||||
|       }) |     } as Partial<CPMMContract>) | ||||||
|     ) |  | ||||||
| 
 | 
 | ||||||
|     const newBalance = user.balance - amount |     const newBalance = user.balance - amount | ||||||
|     const newTotalDeposits = user.totalDeposits - amount |     const newTotalDeposits = user.totalDeposits - amount | ||||||
|  | @ -93,41 +76,3 @@ export const addliquidity = newEndpoint({}, async (req, auth) => { | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| const firestore = admin.firestore() | const firestore = admin.firestore() | ||||||
| 
 |  | ||||||
| export const addHouseLiquidity = (contract: CPMMContract, amount: number) => { |  | ||||||
|   return firestore.runTransaction(async (transaction) => { |  | ||||||
|     const newLiquidityProvisionDoc = firestore |  | ||||||
|       .collection(`contracts/${contract.id}/liquidity`) |  | ||||||
|       .doc() |  | ||||||
| 
 |  | ||||||
|     const providerId = isProd() |  | ||||||
|       ? HOUSE_LIQUIDITY_PROVIDER_ID |  | ||||||
|       : DEV_HOUSE_LIQUIDITY_PROVIDER_ID |  | ||||||
| 
 |  | ||||||
|     const { newLiquidityProvision, newPool, newP, newTotalLiquidity } = |  | ||||||
|       getNewLiquidityProvision( |  | ||||||
|         providerId, |  | ||||||
|         amount, |  | ||||||
|         contract, |  | ||||||
|         newLiquidityProvisionDoc.id |  | ||||||
|       ) |  | ||||||
| 
 |  | ||||||
|     if (newP !== undefined && !isFinite(newP)) { |  | ||||||
|       throw new APIError( |  | ||||||
|         500, |  | ||||||
|         'Liquidity injection rejected due to overflow error.' |  | ||||||
|       ) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     transaction.update( |  | ||||||
|       firestore.doc(`contracts/${contract.id}`), |  | ||||||
|       removeUndefinedProps({ |  | ||||||
|         pool: newPool, |  | ||||||
|         p: newP, |  | ||||||
|         totalLiquidity: newTotalLiquidity, |  | ||||||
|       }) |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     transaction.create(newLiquidityProvisionDoc, newLiquidityProvision) |  | ||||||
|   }) |  | ||||||
| } |  | ||||||
							
								
								
									
										69
									
								
								functions/src/drizzle-liquidity.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								functions/src/drizzle-liquidity.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | ||||||
|  | import * as functions from 'firebase-functions' | ||||||
|  | import * as admin from 'firebase-admin' | ||||||
|  | 
 | ||||||
|  | import { CPMMContract } from '../../common/contract' | ||||||
|  | import { batchedWaitAll } from '../../common/util/promise' | ||||||
|  | import { APIError } from '../../common/api' | ||||||
|  | import { addCpmmLiquidity } from '../../common/calculate-cpmm' | ||||||
|  | import { formatMoneyWithDecimals } from '../../common/util/format' | ||||||
|  | 
 | ||||||
|  | const firestore = admin.firestore() | ||||||
|  | 
 | ||||||
|  | export const drizzleLiquidity = async () => { | ||||||
|  |   const snap = await firestore | ||||||
|  |     .collection('contracts') | ||||||
|  |     .where('subsidyPool', '>', 1e-7) | ||||||
|  |     .get() | ||||||
|  | 
 | ||||||
|  |   const contractIds = snap.docs.map((doc) => doc.id) | ||||||
|  |   console.log('found', contractIds.length, 'markets to drizzle') | ||||||
|  |   console.log() | ||||||
|  | 
 | ||||||
|  |   await batchedWaitAll( | ||||||
|  |     contractIds.map((cid) => () => drizzleMarket(cid)), | ||||||
|  |     10 | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const drizzleLiquidityScheduler = functions.pubsub | ||||||
|  |   .schedule('* * * * *') // every minute
 | ||||||
|  |   .onRun(drizzleLiquidity) | ||||||
|  | 
 | ||||||
|  | const drizzleMarket = async (contractId: string) => { | ||||||
|  |   await firestore.runTransaction(async (trans) => { | ||||||
|  |     const snap = await trans.get(firestore.doc(`contracts/${contractId}`)) | ||||||
|  |     const contract = snap.data() as CPMMContract | ||||||
|  |     const { subsidyPool, pool, p, slug, popularityScore } = contract | ||||||
|  |     if ((subsidyPool ?? 0) < 1e-7) return | ||||||
|  | 
 | ||||||
|  |     const r = Math.random() | ||||||
|  |     const logPopularity = Math.log10((popularityScore ?? 0) + 1) | ||||||
|  |     const v = Math.max(1, Math.min(5, logPopularity)) | ||||||
|  |     const amount = subsidyPool <= 0.5 ? subsidyPool : r * v * 0.01 * subsidyPool | ||||||
|  | 
 | ||||||
|  |     const { newPool, newP } = addCpmmLiquidity(pool, p, amount) | ||||||
|  | 
 | ||||||
|  |     if (!isFinite(newP)) { | ||||||
|  |       throw new APIError( | ||||||
|  |         500, | ||||||
|  |         'Liquidity injection rejected due to overflow error.' | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     await trans.update(firestore.doc(`contracts/${contract.id}`), { | ||||||
|  |       pool: newPool, | ||||||
|  |       p: newP, | ||||||
|  |       subsidyPool: subsidyPool - amount, | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     console.log( | ||||||
|  |       'added subsidy', | ||||||
|  |       formatMoneyWithDecimals(amount), | ||||||
|  |       'of', | ||||||
|  |       formatMoneyWithDecimals(subsidyPool), | ||||||
|  |       'pool to', | ||||||
|  |       slug | ||||||
|  |     ) | ||||||
|  |     console.log() | ||||||
|  |   }) | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								functions/src/helpers/add-house-subsidy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								functions/src/helpers/add-house-subsidy.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | ||||||
|  | import * as admin from 'firebase-admin' | ||||||
|  | 
 | ||||||
|  | import { CPMMContract } from '../../../common/contract' | ||||||
|  | import { isProd } from '../utils' | ||||||
|  | import { | ||||||
|  |   DEV_HOUSE_LIQUIDITY_PROVIDER_ID, | ||||||
|  |   HOUSE_LIQUIDITY_PROVIDER_ID, | ||||||
|  | } from '../../../common/antes' | ||||||
|  | import { getNewLiquidityProvision } from '../../../common/add-liquidity' | ||||||
|  | 
 | ||||||
|  | const firestore = admin.firestore() | ||||||
|  | 
 | ||||||
|  | export const addHouseSubsidy = (contractId: string, amount: number) => { | ||||||
|  |   return firestore.runTransaction(async (transaction) => { | ||||||
|  |     const newLiquidityProvisionDoc = firestore | ||||||
|  |       .collection(`contracts/${contractId}/liquidity`) | ||||||
|  |       .doc() | ||||||
|  | 
 | ||||||
|  |     const providerId = isProd() | ||||||
|  |       ? HOUSE_LIQUIDITY_PROVIDER_ID | ||||||
|  |       : DEV_HOUSE_LIQUIDITY_PROVIDER_ID | ||||||
|  | 
 | ||||||
|  |     const contractDoc = firestore.doc(`contracts/${contractId}`) | ||||||
|  |     const snap = await contractDoc.get() | ||||||
|  |     const contract = snap.data() as CPMMContract | ||||||
|  | 
 | ||||||
|  |     const { newLiquidityProvision, newTotalLiquidity, newSubsidyPool } = | ||||||
|  |       getNewLiquidityProvision( | ||||||
|  |         providerId, | ||||||
|  |         amount, | ||||||
|  |         contract, | ||||||
|  |         newLiquidityProvisionDoc.id | ||||||
|  |       ) | ||||||
|  | 
 | ||||||
|  |     transaction.update(contractDoc, { | ||||||
|  |       subsidyPool: newSubsidyPool, | ||||||
|  |       totalLiquidity: newTotalLiquidity, | ||||||
|  |     } as Partial<CPMMContract>) | ||||||
|  | 
 | ||||||
|  |     transaction.create(newLiquidityProvisionDoc, newLiquidityProvision) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | @ -31,6 +31,7 @@ export * from './reset-weekly-emails-flags' | ||||||
| export * from './on-update-contract-follow' | export * from './on-update-contract-follow' | ||||||
| export * from './on-update-like' | export * from './on-update-like' | ||||||
| export * from './weekly-portfolio-emails' | export * from './weekly-portfolio-emails' | ||||||
|  | export * from './drizzle-liquidity' | ||||||
| 
 | 
 | ||||||
| // v2
 | // v2
 | ||||||
| export * from './health' | export * from './health' | ||||||
|  | @ -44,8 +45,6 @@ export * from './sell-bet' | ||||||
| export * from './sell-shares' | export * from './sell-shares' | ||||||
| export * from './claim-manalink' | export * from './claim-manalink' | ||||||
| export * from './create-market' | export * from './create-market' | ||||||
| export * from './add-liquidity' |  | ||||||
| export * from './withdraw-liquidity' |  | ||||||
| export * from './create-group' | export * from './create-group' | ||||||
| export * from './resolve-market' | export * from './resolve-market' | ||||||
| export * from './unsubscribe' | export * from './unsubscribe' | ||||||
|  | @ -53,6 +52,7 @@ export * from './stripe' | ||||||
| export * from './mana-bonus-email' | export * from './mana-bonus-email' | ||||||
| export * from './close-market' | export * from './close-market' | ||||||
| export * from './update-comment-bounty' | export * from './update-comment-bounty' | ||||||
|  | export * from './add-subsidy' | ||||||
| 
 | 
 | ||||||
| import { health } from './health' | import { health } from './health' | ||||||
| import { transact } from './transact' | import { transact } from './transact' | ||||||
|  | @ -65,9 +65,7 @@ import { sellbet } from './sell-bet' | ||||||
| import { sellshares } from './sell-shares' | import { sellshares } from './sell-shares' | ||||||
| import { claimmanalink } from './claim-manalink' | import { claimmanalink } from './claim-manalink' | ||||||
| import { createmarket } from './create-market' | import { createmarket } from './create-market' | ||||||
| import { addliquidity } from './add-liquidity' |  | ||||||
| import { addcommentbounty, awardcommentbounty } from './update-comment-bounty' | import { addcommentbounty, awardcommentbounty } from './update-comment-bounty' | ||||||
| import { withdrawliquidity } from './withdraw-liquidity' |  | ||||||
| import { creategroup } from './create-group' | import { creategroup } from './create-group' | ||||||
| import { resolvemarket } from './resolve-market' | import { resolvemarket } from './resolve-market' | ||||||
| import { closemarket } from './close-market' | import { closemarket } from './close-market' | ||||||
|  | @ -78,6 +76,7 @@ import { acceptchallenge } from './accept-challenge' | ||||||
| import { createpost } from './create-post' | import { createpost } from './create-post' | ||||||
| import { savetwitchcredentials } from './save-twitch-credentials' | import { savetwitchcredentials } from './save-twitch-credentials' | ||||||
| import { updatemetrics } from './update-metrics' | import { updatemetrics } from './update-metrics' | ||||||
|  | import { addsubsidy } from './add-subsidy' | ||||||
| 
 | 
 | ||||||
| const toCloudFunction = ({ opts, handler }: EndpointDefinition) => { | const toCloudFunction = ({ opts, handler }: EndpointDefinition) => { | ||||||
|   return onRequest(opts, handler as any) |   return onRequest(opts, handler as any) | ||||||
|  | @ -93,10 +92,9 @@ const sellBetFunction = toCloudFunction(sellbet) | ||||||
| const sellSharesFunction = toCloudFunction(sellshares) | const sellSharesFunction = toCloudFunction(sellshares) | ||||||
| const claimManalinkFunction = toCloudFunction(claimmanalink) | const claimManalinkFunction = toCloudFunction(claimmanalink) | ||||||
| const createMarketFunction = toCloudFunction(createmarket) | const createMarketFunction = toCloudFunction(createmarket) | ||||||
| const addLiquidityFunction = toCloudFunction(addliquidity) | const addSubsidyFunction = toCloudFunction(addsubsidy) | ||||||
| const addCommentBounty = toCloudFunction(addcommentbounty) | const addCommentBounty = toCloudFunction(addcommentbounty) | ||||||
| const awardCommentBounty = toCloudFunction(awardcommentbounty) | const awardCommentBounty = toCloudFunction(awardcommentbounty) | ||||||
| const withdrawLiquidityFunction = toCloudFunction(withdrawliquidity) |  | ||||||
| const createGroupFunction = toCloudFunction(creategroup) | const createGroupFunction = toCloudFunction(creategroup) | ||||||
| const resolveMarketFunction = toCloudFunction(resolvemarket) | const resolveMarketFunction = toCloudFunction(resolvemarket) | ||||||
| const closeMarketFunction = toCloudFunction(closemarket) | const closeMarketFunction = toCloudFunction(closemarket) | ||||||
|  | @ -121,8 +119,7 @@ export { | ||||||
|   sellSharesFunction as sellshares, |   sellSharesFunction as sellshares, | ||||||
|   claimManalinkFunction as claimmanalink, |   claimManalinkFunction as claimmanalink, | ||||||
|   createMarketFunction as createmarket, |   createMarketFunction as createmarket, | ||||||
|   addLiquidityFunction as addliquidity, |   addSubsidyFunction as addsubsidy, | ||||||
|   withdrawLiquidityFunction as withdrawliquidity, |  | ||||||
|   createGroupFunction as creategroup, |   createGroupFunction as creategroup, | ||||||
|   resolveMarketFunction as resolvemarket, |   resolveMarketFunction as resolvemarket, | ||||||
|   closeMarketFunction as closemarket, |   closeMarketFunction as closemarket, | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ import { | ||||||
|   BETTING_STREAK_BONUS_MAX, |   BETTING_STREAK_BONUS_MAX, | ||||||
|   BETTING_STREAK_RESET_HOUR, |   BETTING_STREAK_RESET_HOUR, | ||||||
|   UNIQUE_BETTOR_BONUS_AMOUNT, |   UNIQUE_BETTOR_BONUS_AMOUNT, | ||||||
|  |   UNIQUE_BETTOR_LIQUIDITY, | ||||||
| } from '../../common/economy' | } from '../../common/economy' | ||||||
| import { | import { | ||||||
|   DEV_HOUSE_LIQUIDITY_PROVIDER_ID, |   DEV_HOUSE_LIQUIDITY_PROVIDER_ID, | ||||||
|  | @ -34,6 +35,7 @@ import { APIError } from '../../common/api' | ||||||
| import { User } from '../../common/user' | import { User } from '../../common/user' | ||||||
| import { DAY_MS } from '../../common/util/time' | import { DAY_MS } from '../../common/util/time' | ||||||
| import { BettingStreakBonusTxn, UniqueBettorBonusTxn } from '../../common/txn' | import { BettingStreakBonusTxn, UniqueBettorBonusTxn } from '../../common/txn' | ||||||
|  | import { addHouseSubsidy } from './helpers/add-house-subsidy' | ||||||
| import { | import { | ||||||
|   StreakerBadge, |   StreakerBadge, | ||||||
|   streakerBadgeRarityThresholds, |   streakerBadgeRarityThresholds, | ||||||
|  | @ -108,7 +110,7 @@ const updateBettingStreak = async ( | ||||||
| 
 | 
 | ||||||
|     const newBettingStreak = (bettor?.currentBettingStreak ?? 0) + 1 |     const newBettingStreak = (bettor?.currentBettingStreak ?? 0) + 1 | ||||||
|     // Otherwise, add 1 to their betting streak
 |     // Otherwise, add 1 to their betting streak
 | ||||||
|     await trans.update(userDoc, { |     trans.update(userDoc, { | ||||||
|       currentBettingStreak: newBettingStreak, |       currentBettingStreak: newBettingStreak, | ||||||
|       lastBetTime: bet.createdTime, |       lastBetTime: bet.createdTime, | ||||||
|     }) |     }) | ||||||
|  | @ -198,7 +200,7 @@ const updateUniqueBettorsAndGiveCreatorBonus = async ( | ||||||
|         log(`Got ${previousUniqueBettorIds} unique bettors`) |         log(`Got ${previousUniqueBettorIds} unique bettors`) | ||||||
|         isNewUniqueBettor && log(`And a new unique bettor ${bettor.id}`) |         isNewUniqueBettor && log(`And a new unique bettor ${bettor.id}`) | ||||||
| 
 | 
 | ||||||
|         await trans.update(contractDoc, { |         trans.update(contractDoc, { | ||||||
|           uniqueBettorIds: newUniqueBettorIds, |           uniqueBettorIds: newUniqueBettorIds, | ||||||
|           uniqueBettorCount: newUniqueBettorIds.length, |           uniqueBettorCount: newUniqueBettorIds.length, | ||||||
|         }) |         }) | ||||||
|  | @ -211,8 +213,13 @@ const updateUniqueBettorsAndGiveCreatorBonus = async ( | ||||||
|       return { newUniqueBettorIds } |       return { newUniqueBettorIds } | ||||||
|     } |     } | ||||||
|   ) |   ) | ||||||
|  | 
 | ||||||
|   if (!newUniqueBettorIds) return |   if (!newUniqueBettorIds) return | ||||||
| 
 | 
 | ||||||
|  |   if (oldContract.mechanism === 'cpmm-1') { | ||||||
|  |     await addHouseSubsidy(oldContract.id, UNIQUE_BETTOR_LIQUIDITY) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   const bonusTxnDetails = { |   const bonusTxnDetails = { | ||||||
|     contractId: oldContract.id, |     contractId: oldContract.id, | ||||||
|     uniqueNewBettorId: bettor.id, |     uniqueNewBettorId: bettor.id, | ||||||
|  | @ -222,7 +229,9 @@ const updateUniqueBettorsAndGiveCreatorBonus = async ( | ||||||
|     : DEV_HOUSE_LIQUIDITY_PROVIDER_ID |     : DEV_HOUSE_LIQUIDITY_PROVIDER_ID | ||||||
|   const fromSnap = await firestore.doc(`users/${fromUserId}`).get() |   const fromSnap = await firestore.doc(`users/${fromUserId}`).get() | ||||||
|   if (!fromSnap.exists) throw new APIError(400, 'From user not found.') |   if (!fromSnap.exists) throw new APIError(400, 'From user not found.') | ||||||
|  | 
 | ||||||
|   const fromUser = fromSnap.data() as User |   const fromUser = fromSnap.data() as User | ||||||
|  | 
 | ||||||
|   const result = await firestore.runTransaction(async (trans) => { |   const result = await firestore.runTransaction(async (trans) => { | ||||||
|     const bonusTxn: TxnData = { |     const bonusTxn: TxnData = { | ||||||
|       fromId: fromUser.id, |       fromId: fromUser.id, | ||||||
|  | @ -235,7 +244,9 @@ const updateUniqueBettorsAndGiveCreatorBonus = async ( | ||||||
|       description: JSON.stringify(bonusTxnDetails), |       description: JSON.stringify(bonusTxnDetails), | ||||||
|       data: bonusTxnDetails, |       data: bonusTxnDetails, | ||||||
|     } as Omit<UniqueBettorBonusTxn, 'id' | 'createdTime'> |     } as Omit<UniqueBettorBonusTxn, 'id' | 'createdTime'> | ||||||
|  | 
 | ||||||
|     const { status, message, txn } = await runTxn(trans, bonusTxn) |     const { status, message, txn } = await runTxn(trans, bonusTxn) | ||||||
|  | 
 | ||||||
|     return { status, newUniqueBettorIds, message, txn } |     return { status, newUniqueBettorIds, message, txn } | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,15 @@ import { | ||||||
|   RESOLUTIONS, |   RESOLUTIONS, | ||||||
| } from '../../common/contract' | } from '../../common/contract' | ||||||
| import { Bet } from '../../common/bet' | 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 { | import { | ||||||
|   getLoanPayouts, |   getLoanPayouts, | ||||||
|   getPayouts, |   getPayouts, | ||||||
|  | @ -145,6 +153,7 @@ export const resolvemarket = newEndpoint(opts, async (req, auth) => { | ||||||
|       resolutions, |       resolutions, | ||||||
|       collectedFees, |       collectedFees, | ||||||
|     }), |     }), | ||||||
|  |     subsidyPool: 0, | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   await contractDoc.update(updatedContract) |   await contractDoc.update(updatedContract) | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								functions/src/scripts/drizzle.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								functions/src/scripts/drizzle.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | import { initAdmin } from './script-init' | ||||||
|  | initAdmin() | ||||||
|  | 
 | ||||||
|  | import { drizzleLiquidity } from '../drizzle-liquidity' | ||||||
|  | 
 | ||||||
|  | if (require.main === module) { | ||||||
|  |   drizzleLiquidity().then(() => process.exit()) | ||||||
|  | } | ||||||
|  | @ -19,8 +19,6 @@ import { sellbet } from './sell-bet' | ||||||
| import { sellshares } from './sell-shares' | import { sellshares } from './sell-shares' | ||||||
| import { claimmanalink } from './claim-manalink' | import { claimmanalink } from './claim-manalink' | ||||||
| import { createmarket } from './create-market' | import { createmarket } from './create-market' | ||||||
| import { addliquidity } from './add-liquidity' |  | ||||||
| import { withdrawliquidity } from './withdraw-liquidity' |  | ||||||
| import { creategroup } from './create-group' | import { creategroup } from './create-group' | ||||||
| import { resolvemarket } from './resolve-market' | import { resolvemarket } from './resolve-market' | ||||||
| import { unsubscribe } from './unsubscribe' | import { unsubscribe } from './unsubscribe' | ||||||
|  | @ -61,10 +59,8 @@ addJsonEndpointRoute('/sellbet', sellbet) | ||||||
| addJsonEndpointRoute('/sellshares', sellshares) | addJsonEndpointRoute('/sellshares', sellshares) | ||||||
| addJsonEndpointRoute('/claimmanalink', claimmanalink) | addJsonEndpointRoute('/claimmanalink', claimmanalink) | ||||||
| addJsonEndpointRoute('/createmarket', createmarket) | addJsonEndpointRoute('/createmarket', createmarket) | ||||||
| addJsonEndpointRoute('/addliquidity', addliquidity) |  | ||||||
| addJsonEndpointRoute('/addCommentBounty', addcommentbounty) | addJsonEndpointRoute('/addCommentBounty', addcommentbounty) | ||||||
| addJsonEndpointRoute('/awardCommentBounty', awardcommentbounty) | addJsonEndpointRoute('/awardCommentBounty', awardcommentbounty) | ||||||
| addJsonEndpointRoute('/withdrawliquidity', withdrawliquidity) |  | ||||||
| addJsonEndpointRoute('/creategroup', creategroup) | addJsonEndpointRoute('/creategroup', creategroup) | ||||||
| addJsonEndpointRoute('/resolvemarket', resolvemarket) | addJsonEndpointRoute('/resolvemarket', resolvemarket) | ||||||
| addJsonEndpointRoute('/unsubscribe', unsubscribe) | addJsonEndpointRoute('/unsubscribe', unsubscribe) | ||||||
|  |  | ||||||
|  | @ -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<User>) |  | ||||||
| 
 |  | ||||||
|       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<Bet, 'id'>) |  | ||||||
|         ) |  | ||||||
|         .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() |  | ||||||
|  | @ -7,7 +7,6 @@ import { capitalize } from 'lodash' | ||||||
| import { Contract } from 'common/contract' | import { Contract } from 'common/contract' | ||||||
| import { formatMoney, formatPercent } from 'common/util/format' | import { formatMoney, formatPercent } from 'common/util/format' | ||||||
| import { contractPool, updateContract } from 'web/lib/firebase/contracts' | import { contractPool, updateContract } from 'web/lib/firebase/contracts' | ||||||
| import { LiquidityBountyPanel } from 'web/components/contract/liquidity-bounty-panel' |  | ||||||
| import { Col } from '../layout/col' | import { Col } from '../layout/col' | ||||||
| import { Modal } from '../layout/modal' | import { Modal } from '../layout/modal' | ||||||
| import { Title } from '../title' | import { Title } from '../title' | ||||||
|  | @ -55,6 +54,7 @@ export function ContractInfoDialog(props: { | ||||||
|     outcomeType, |     outcomeType, | ||||||
|     id, |     id, | ||||||
|     elasticity, |     elasticity, | ||||||
|  |     pool, | ||||||
|   } = contract |   } = contract | ||||||
| 
 | 
 | ||||||
|   const typeDisplay = |   const typeDisplay = | ||||||
|  | @ -172,10 +172,25 @@ export function ContractInfoDialog(props: { | ||||||
|               </tr> |               </tr> | ||||||
| 
 | 
 | ||||||
|               <tr> |               <tr> | ||||||
|  |                 <td>Liquidity subsidies</td> | ||||||
|                 <td> |                 <td> | ||||||
|                   {mechanism === 'cpmm-1' ? 'Liquidity pool' : 'Betting pool'} |                   {mechanism === 'cpmm-1' | ||||||
|  |                     ? formatMoney(contract.totalLiquidity) | ||||||
|  |                     : formatMoney(100)} | ||||||
|  |                 </td> | ||||||
|  |               </tr> | ||||||
|  | 
 | ||||||
|  |               <tr> | ||||||
|  |                 <td>Pool</td> | ||||||
|  |                 <td> | ||||||
|  |                   {mechanism === 'cpmm-1' && outcomeType === 'BINARY' | ||||||
|  |                     ? `${Math.round(pool.YES)} YES, ${Math.round(pool.NO)} NO` | ||||||
|  |                     : mechanism === 'cpmm-1' && outcomeType === 'PSEUDO_NUMERIC' | ||||||
|  |                     ? `${Math.round(pool.YES)} HIGHER, ${Math.round( | ||||||
|  |                         pool.NO | ||||||
|  |                       )} LOWER` | ||||||
|  |                     : contractPool(contract)} | ||||||
|                 </td> |                 </td> | ||||||
|                 <td>{contractPool(contract)}</td> |  | ||||||
|               </tr> |               </tr> | ||||||
| 
 | 
 | ||||||
|               {/* Show a path to Firebase if user is an admin, or we're on localhost */} |               {/* Show a path to Firebase if user is an admin, or we're on localhost */} | ||||||
|  | @ -228,7 +243,6 @@ export function ContractInfoDialog(props: { | ||||||
|           <Row className="flex-wrap"> |           <Row className="flex-wrap"> | ||||||
|             <DuplicateContractButton contract={contract} /> |             <DuplicateContractButton contract={contract} /> | ||||||
|           </Row> |           </Row> | ||||||
|           {!contract.resolution && <LiquidityBountyPanel contract={contract} />} |  | ||||||
|         </Col> |         </Col> | ||||||
|       </Modal> |       </Modal> | ||||||
|     </> |     </> | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import { FollowMarketButton } from 'web/components/follow-market-button' | ||||||
| import { LikeMarketButton } from 'web/components/contract/like-market-button' | import { LikeMarketButton } from 'web/components/contract/like-market-button' | ||||||
| import { ContractInfoDialog } from 'web/components/contract/contract-info-dialog' | import { ContractInfoDialog } from 'web/components/contract/contract-info-dialog' | ||||||
| import { Tooltip } from '../tooltip' | import { Tooltip } from '../tooltip' | ||||||
|  | import { LiquidityButton } from './liquidity-button' | ||||||
| 
 | 
 | ||||||
| export function ExtraContractActionsRow(props: { contract: Contract }) { | export function ExtraContractActionsRow(props: { contract: Contract }) { | ||||||
|   const { contract } = props |   const { contract } = props | ||||||
|  | @ -18,6 +19,9 @@ export function ExtraContractActionsRow(props: { contract: Contract }) { | ||||||
|   return ( |   return ( | ||||||
|     <Row> |     <Row> | ||||||
|       <FollowMarketButton contract={contract} user={user} /> |       <FollowMarketButton contract={contract} user={user} /> | ||||||
|  |       {contract.mechanism === 'cpmm-1' && ( | ||||||
|  |         <LiquidityButton contract={contract} user={user} /> | ||||||
|  |       )} | ||||||
|       <LikeMarketButton contract={contract} user={user} /> |       <LikeMarketButton contract={contract} user={user} /> | ||||||
|       <Tooltip text="Share" placement="bottom" noTap noFade> |       <Tooltip text="Share" placement="bottom" noTap noFade> | ||||||
|         <Button |         <Button | ||||||
|  |  | ||||||
|  | @ -1,248 +0,0 @@ | ||||||
| import clsx from 'clsx' |  | ||||||
| import { useEffect, useState } from 'react' |  | ||||||
| 
 |  | ||||||
| import { Contract, CPMMContract } from 'common/contract' |  | ||||||
| import { formatMoney } from 'common/util/format' |  | ||||||
| import { useUser } from 'web/hooks/use-user' |  | ||||||
| import { addLiquidity, withdrawLiquidity } from 'web/lib/firebase/api' |  | ||||||
| import { AmountInput } from 'web/components/amount-input' |  | ||||||
| import { Row } from 'web/components/layout/row' |  | ||||||
| import { useUserLiquidity } from 'web/hooks/use-liquidity' |  | ||||||
| import { Tabs } from 'web/components/layout/tabs' |  | ||||||
| import { NoLabel, YesLabel } from 'web/components/outcome-label' |  | ||||||
| import { Col } from 'web/components/layout/col' |  | ||||||
| import { track } from 'web/lib/service/analytics' |  | ||||||
| import { InfoTooltip } from 'web/components/info-tooltip' |  | ||||||
| import { BETTORS, PRESENT_BET } from 'common/user' |  | ||||||
| import { buildArray } from 'common/util/array' |  | ||||||
| import { useAdmin } from 'web/hooks/use-admin' |  | ||||||
| import { AlertBox } from '../alert-box' |  | ||||||
| import { Spacer } from '../layout/spacer' |  | ||||||
| 
 |  | ||||||
| export function LiquidityBountyPanel(props: { contract: Contract }) { |  | ||||||
|   const { contract } = props |  | ||||||
| 
 |  | ||||||
|   const isCPMM = contract.mechanism === 'cpmm-1' |  | ||||||
|   const user = useUser() |  | ||||||
|   // eslint-disable-next-line react-hooks/rules-of-hooks
 |  | ||||||
|   const lpShares = isCPMM && useUserLiquidity(contract, user?.id ?? '') |  | ||||||
| 
 |  | ||||||
|   const [showWithdrawal, setShowWithdrawal] = useState(false) |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     if (!showWithdrawal && lpShares && lpShares.YES && lpShares.NO) |  | ||||||
|       setShowWithdrawal(true) |  | ||||||
|   }, [showWithdrawal, lpShares]) |  | ||||||
| 
 |  | ||||||
|   const isCreator = user?.id === contract.creatorId |  | ||||||
|   const isAdmin = useAdmin() |  | ||||||
| 
 |  | ||||||
|   if (!isCreator && !isAdmin && !showWithdrawal) return <></> |  | ||||||
| 
 |  | ||||||
|   return ( |  | ||||||
|     <Tabs |  | ||||||
|       tabs={buildArray( |  | ||||||
|         (isCreator || isAdmin) && |  | ||||||
|           isCPMM && { |  | ||||||
|             title: (isAdmin ? '[Admin] ' : '') + 'Subsidize', |  | ||||||
|             content: <AddLiquidityPanel contract={contract} />, |  | ||||||
|           }, |  | ||||||
|         showWithdrawal && |  | ||||||
|           isCPMM && { |  | ||||||
|             title: 'Withdraw', |  | ||||||
|             content: ( |  | ||||||
|               <WithdrawLiquidityPanel |  | ||||||
|                 contract={contract} |  | ||||||
|                 lpShares={lpShares as { YES: number; NO: number }} |  | ||||||
|               /> |  | ||||||
|             ), |  | ||||||
|           }, |  | ||||||
| 
 |  | ||||||
|         (isCreator || isAdmin) && |  | ||||||
|           isCPMM && { |  | ||||||
|             title: 'Pool', |  | ||||||
|             content: <ViewLiquidityPanel contract={contract} />, |  | ||||||
|           } |  | ||||||
|       )} |  | ||||||
|     /> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function AddLiquidityPanel(props: { contract: CPMMContract }) { |  | ||||||
|   const { contract } = props |  | ||||||
|   const { id: contractId, slug } = contract |  | ||||||
| 
 |  | ||||||
|   const user = useUser() |  | ||||||
| 
 |  | ||||||
|   const [amount, setAmount] = useState<number | undefined>(undefined) |  | ||||||
|   const [error, setError] = useState<string | undefined>(undefined) |  | ||||||
|   const [isSuccess, setIsSuccess] = useState(false) |  | ||||||
|   const [isLoading, setIsLoading] = useState(false) |  | ||||||
| 
 |  | ||||||
|   const onAmountChange = (amount: number | undefined) => { |  | ||||||
|     setIsSuccess(false) |  | ||||||
|     setAmount(amount) |  | ||||||
| 
 |  | ||||||
|     // Check for errors.
 |  | ||||||
|     if (amount !== undefined) { |  | ||||||
|       if (user && user.balance < amount) { |  | ||||||
|         setError('Insufficient balance') |  | ||||||
|       } else if (amount < 1) { |  | ||||||
|         setError('Minimum amount: ' + formatMoney(1)) |  | ||||||
|       } else { |  | ||||||
|         setError(undefined) |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   const submit = () => { |  | ||||||
|     if (!amount) return |  | ||||||
| 
 |  | ||||||
|     setIsLoading(true) |  | ||||||
|     setIsSuccess(false) |  | ||||||
| 
 |  | ||||||
|     addLiquidity({ amount, contractId }) |  | ||||||
|       .then((_) => { |  | ||||||
|         setIsSuccess(true) |  | ||||||
|         setError(undefined) |  | ||||||
|         setIsLoading(false) |  | ||||||
|       }) |  | ||||||
|       .catch((_) => setError('Server error')) |  | ||||||
| 
 |  | ||||||
|     track('add liquidity', { amount, contractId, slug }) |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return ( |  | ||||||
|     <> |  | ||||||
|       <div className="mb-4 text-gray-500"> |  | ||||||
|         Contribute your M$ to make this market more accurate.{' '} |  | ||||||
|         <InfoTooltip |  | ||||||
|           text={`More liquidity stabilizes the market, encouraging ${BETTORS} to ${PRESENT_BET}.`} |  | ||||||
|         /> |  | ||||||
|       </div> |  | ||||||
| 
 |  | ||||||
|       <Row> |  | ||||||
|         <AmountInput |  | ||||||
|           amount={amount} |  | ||||||
|           onChange={onAmountChange} |  | ||||||
|           label="M$" |  | ||||||
|           error={error} |  | ||||||
|           disabled={isLoading} |  | ||||||
|           inputClassName="w-28" |  | ||||||
|         /> |  | ||||||
|         <button |  | ||||||
|           className={clsx('btn btn-primary ml-2', isLoading && 'btn-disabled')} |  | ||||||
|           onClick={submit} |  | ||||||
|           disabled={isLoading} |  | ||||||
|         > |  | ||||||
|           Add |  | ||||||
|         </button> |  | ||||||
|       </Row> |  | ||||||
| 
 |  | ||||||
|       {isSuccess && amount && ( |  | ||||||
|         <div>Success! Added {formatMoney(amount)} in liquidity.</div> |  | ||||||
|       )} |  | ||||||
| 
 |  | ||||||
|       {isLoading && <div>Processing...</div>} |  | ||||||
| 
 |  | ||||||
|       <Spacer h={2} /> |  | ||||||
|       <AlertBox |  | ||||||
|         title="Withdrawals ending" |  | ||||||
|         text="Manifold is moving to a new system for handling subsidization. As part of this process, liquidity withdrawals will be disabled shortly. Feel free to withdraw any outstanding liquidity you've added now." |  | ||||||
|       /> |  | ||||||
|     </> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function ViewLiquidityPanel(props: { contract: CPMMContract }) { |  | ||||||
|   const { contract } = props |  | ||||||
|   const { pool } = contract |  | ||||||
|   const { YES: yesShares, NO: noShares } = pool |  | ||||||
| 
 |  | ||||||
|   return ( |  | ||||||
|     <Col className="mb-4"> |  | ||||||
|       <div className="mb-4 text-gray-500"> |  | ||||||
|         The liquidity pool for this market currently contains: |  | ||||||
|       </div> |  | ||||||
|       <span> |  | ||||||
|         {yesShares.toFixed(2)} <YesLabel /> shares |  | ||||||
|       </span> |  | ||||||
| 
 |  | ||||||
|       <span> |  | ||||||
|         {noShares.toFixed(2)} <NoLabel /> shares |  | ||||||
|       </span> |  | ||||||
|     </Col> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function WithdrawLiquidityPanel(props: { |  | ||||||
|   contract: CPMMContract |  | ||||||
|   lpShares: { YES: number; NO: number } |  | ||||||
| }) { |  | ||||||
|   const { contract, lpShares } = props |  | ||||||
|   const { YES: yesShares, NO: noShares } = lpShares |  | ||||||
| 
 |  | ||||||
|   const [_error, setError] = useState<string | undefined>(undefined) |  | ||||||
|   const [isSuccess, setIsSuccess] = useState(false) |  | ||||||
|   const [isLoading, setIsLoading] = useState(false) |  | ||||||
| 
 |  | ||||||
|   const submit = () => { |  | ||||||
|     setIsLoading(true) |  | ||||||
|     setIsSuccess(false) |  | ||||||
| 
 |  | ||||||
|     withdrawLiquidity({ contractId: contract.id }) |  | ||||||
|       .then((_) => { |  | ||||||
|         setIsSuccess(true) |  | ||||||
|         setError(undefined) |  | ||||||
|         setIsLoading(false) |  | ||||||
|       }) |  | ||||||
|       .catch((_) => setError('Server error')) |  | ||||||
| 
 |  | ||||||
|     track('withdraw liquidity') |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (isSuccess) |  | ||||||
|     return ( |  | ||||||
|       <div className="text-gray-500"> |  | ||||||
|         Success! Your liquidity was withdrawn. |  | ||||||
|       </div> |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|   if (!yesShares && !noShares) |  | ||||||
|     return ( |  | ||||||
|       <div className="text-gray-500"> |  | ||||||
|         You do not have any liquidity positions to withdraw. |  | ||||||
|       </div> |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|   return ( |  | ||||||
|     <Col> |  | ||||||
|       <div className="mb-4 text-gray-500"> |  | ||||||
|         Your liquidity position is currently: |  | ||||||
|       </div> |  | ||||||
| 
 |  | ||||||
|       <span> |  | ||||||
|         {yesShares.toFixed(2)} <YesLabel /> shares |  | ||||||
|       </span> |  | ||||||
| 
 |  | ||||||
|       <span> |  | ||||||
|         {noShares.toFixed(2)} <NoLabel /> shares |  | ||||||
|       </span> |  | ||||||
| 
 |  | ||||||
|       <Row className="mt-4 mb-2"> |  | ||||||
|         <button |  | ||||||
|           className={clsx( |  | ||||||
|             'btn btn-outline btn-sm ml-2', |  | ||||||
|             isLoading && 'btn-disabled' |  | ||||||
|           )} |  | ||||||
|           onClick={submit} |  | ||||||
|           disabled={isLoading} |  | ||||||
|         > |  | ||||||
|           Withdraw |  | ||||||
|         </button> |  | ||||||
|       </Row> |  | ||||||
| 
 |  | ||||||
|       {isLoading && <div>Processing...</div>} |  | ||||||
|     </Col> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
							
								
								
									
										92
									
								
								web/components/contract/liquidity-button.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								web/components/contract/liquidity-button.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | ||||||
|  | import { useState } from 'react' | ||||||
|  | import clsx from 'clsx' | ||||||
|  | 
 | ||||||
|  | import { Button } from 'web/components/button' | ||||||
|  | import { formatMoney, shortFormatNumber } from 'common/util/format' | ||||||
|  | import { Col } from 'web/components/layout/col' | ||||||
|  | import { Tooltip } from '../tooltip' | ||||||
|  | import { CPMMContract } from 'common/contract' | ||||||
|  | import { User } from 'common/user' | ||||||
|  | import { useLiquidity } from 'web/hooks/use-liquidity' | ||||||
|  | import { LiquidityModal } from './liquidity-modal' | ||||||
|  | 
 | ||||||
|  | export function LiquidityButton(props: { | ||||||
|  |   contract: CPMMContract | ||||||
|  |   user: User | undefined | null | ||||||
|  | }) { | ||||||
|  |   const { contract, user } = props | ||||||
|  |   const { totalLiquidity: total } = contract | ||||||
|  | 
 | ||||||
|  |   const lp = useLiquidity(contract.id) | ||||||
|  |   const userActive = lp?.find((l) => l.userId === user?.id) !== undefined | ||||||
|  | 
 | ||||||
|  |   const [open, setOpen] = useState(false) | ||||||
|  | 
 | ||||||
|  |   const disabled = | ||||||
|  |     contract.isResolved || (contract.closeTime ?? Infinity) < Date.now() | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Tooltip | ||||||
|  |       text={`${formatMoney(total)} in liquidity subsidies`} | ||||||
|  |       placement="bottom" | ||||||
|  |       noTap | ||||||
|  |       noFade | ||||||
|  |     > | ||||||
|  |       <LiquidityIconButton | ||||||
|  |         total={total} | ||||||
|  |         userActive={userActive} | ||||||
|  |         onClick={() => setOpen(true)} | ||||||
|  |         disabled={disabled} | ||||||
|  |       /> | ||||||
|  |       <LiquidityModal contract={contract} isOpen={open} setOpen={setOpen} /> | ||||||
|  |     </Tooltip> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function LiquidityIconButton(props: { | ||||||
|  |   total: number | ||||||
|  |   onClick: () => void | ||||||
|  |   userActive: boolean | ||||||
|  |   isCompact?: boolean | ||||||
|  |   disabled?: boolean | ||||||
|  | }) { | ||||||
|  |   const { total, userActive, isCompact, onClick, disabled } = props | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Button | ||||||
|  |       size={'sm'} | ||||||
|  |       className={clsx( | ||||||
|  |         'max-w-xs self-center pt-1', | ||||||
|  |         isCompact && 'px-0 py-0', | ||||||
|  |         disabled && 'hover:bg-inherit' | ||||||
|  |       )} | ||||||
|  |       color={'gray-white'} | ||||||
|  |       onClick={onClick} | ||||||
|  |       disabled={disabled} | ||||||
|  |     > | ||||||
|  |       <Col className={'relative items-center sm:flex-row'}> | ||||||
|  |         <span | ||||||
|  |           className={clsx( | ||||||
|  |             'text-xl sm:text-2xl', | ||||||
|  |             total > 0 ? 'mr-2' : '', | ||||||
|  |             userActive ? '' : 'grayscale' | ||||||
|  |           )} | ||||||
|  |         > | ||||||
|  |           💧 | ||||||
|  |         </span> | ||||||
|  |         {total > 0 && ( | ||||||
|  |           <div | ||||||
|  |             className={clsx( | ||||||
|  |               'bg-greyscale-5 absolute ml-3.5 mt-2 h-4 w-4 rounded-full align-middle text-white sm:mt-3 sm:h-5 sm:w-5 sm:px-1', | ||||||
|  |               total > 99 | ||||||
|  |                 ? 'text-[0.4rem] sm:text-[0.5rem]' | ||||||
|  |                 : 'sm:text-2xs text-[0.5rem]' | ||||||
|  |             )} | ||||||
|  |           > | ||||||
|  |             {shortFormatNumber(total)} | ||||||
|  |           </div> | ||||||
|  |         )} | ||||||
|  |       </Col> | ||||||
|  |     </Button> | ||||||
|  |   ) | ||||||
|  | } | ||||||
							
								
								
									
										108
									
								
								web/components/contract/liquidity-modal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								web/components/contract/liquidity-modal.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,108 @@ | ||||||
|  | import { CPMMContract } from 'common/contract' | ||||||
|  | import { formatMoney } from 'common/util/format' | ||||||
|  | import { useState } from 'react' | ||||||
|  | import { useUser } from 'web/hooks/use-user' | ||||||
|  | import { addSubsidy } from 'web/lib/firebase/api' | ||||||
|  | import { track } from 'web/lib/service/analytics' | ||||||
|  | import { AmountInput } from '../amount-input' | ||||||
|  | import { Button } from '../button' | ||||||
|  | import { InfoTooltip } from '../info-tooltip' | ||||||
|  | import { Col } from '../layout/col' | ||||||
|  | import { Modal } from '../layout/modal' | ||||||
|  | import { Row } from '../layout/row' | ||||||
|  | import { Title } from '../title' | ||||||
|  | 
 | ||||||
|  | export function LiquidityModal(props: { | ||||||
|  |   contract: CPMMContract | ||||||
|  |   isOpen: boolean | ||||||
|  |   setOpen: (open: boolean) => void | ||||||
|  | }) { | ||||||
|  |   const { contract, isOpen, setOpen } = props | ||||||
|  |   const { totalLiquidity } = contract | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Modal open={isOpen} setOpen={setOpen} size="sm"> | ||||||
|  |       <Col className="gap-2.5 rounded  bg-white p-4 pb-8 sm:gap-4"> | ||||||
|  |         <Title className="!mt-0 !mb-2" text="💧 Add a subsidy" /> | ||||||
|  | 
 | ||||||
|  |         <div>Total liquidity subsidies: {formatMoney(totalLiquidity)}</div> | ||||||
|  |         <AddLiquidityPanel contract={contract as CPMMContract} /> | ||||||
|  |       </Col> | ||||||
|  |     </Modal> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function AddLiquidityPanel(props: { contract: CPMMContract }) { | ||||||
|  |   const { contract } = props | ||||||
|  |   const { id: contractId, slug } = contract | ||||||
|  | 
 | ||||||
|  |   const user = useUser() | ||||||
|  | 
 | ||||||
|  |   const [amount, setAmount] = useState<number | undefined>(undefined) | ||||||
|  |   const [error, setError] = useState<string | undefined>(undefined) | ||||||
|  |   const [isSuccess, setIsSuccess] = useState(false) | ||||||
|  |   const [isLoading, setIsLoading] = useState(false) | ||||||
|  | 
 | ||||||
|  |   const onAmountChange = (amount: number | undefined) => { | ||||||
|  |     setIsSuccess(false) | ||||||
|  |     setAmount(amount) | ||||||
|  | 
 | ||||||
|  |     // Check for errors.
 | ||||||
|  |     if (amount !== undefined) { | ||||||
|  |       if (user && user.balance < amount) { | ||||||
|  |         setError('Insufficient balance') | ||||||
|  |       } else if (amount < 1) { | ||||||
|  |         setError('Minimum amount: ' + formatMoney(1)) | ||||||
|  |       } else { | ||||||
|  |         setError(undefined) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const submit = () => { | ||||||
|  |     if (!amount) return | ||||||
|  | 
 | ||||||
|  |     setIsLoading(true) | ||||||
|  |     setIsSuccess(false) | ||||||
|  | 
 | ||||||
|  |     addSubsidy({ amount, contractId }) | ||||||
|  |       .then((_) => { | ||||||
|  |         setIsSuccess(true) | ||||||
|  |         setError(undefined) | ||||||
|  |         setIsLoading(false) | ||||||
|  |       }) | ||||||
|  |       .catch((_) => setError('Server error')) | ||||||
|  | 
 | ||||||
|  |     track('add liquidity', { amount, contractId, slug }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       <div className="mb-4 text-gray-500"> | ||||||
|  |         Contribute your M$ to make this market more accurate by subsidizing | ||||||
|  |         trading.{' '} | ||||||
|  |         <InfoTooltip text="Liquidity is how much money traders can make if they're right. The more traders can earn, the greater the incentive to find the correct probability." /> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <Row> | ||||||
|  |         <AmountInput | ||||||
|  |           amount={amount} | ||||||
|  |           onChange={onAmountChange} | ||||||
|  |           label="M$" | ||||||
|  |           error={error} | ||||||
|  |           disabled={isLoading} | ||||||
|  |           inputClassName="w-16 mr-4" | ||||||
|  |         /> | ||||||
|  |         <Button size="md" color="blue" onClick={submit} disabled={isLoading}> | ||||||
|  |           Add | ||||||
|  |         </Button> | ||||||
|  |       </Row> | ||||||
|  | 
 | ||||||
|  |       {isSuccess && amount && ( | ||||||
|  |         <div>Success! Added {formatMoney(amount)} in liquidity.</div> | ||||||
|  |       )} | ||||||
|  | 
 | ||||||
|  |       {isLoading && <div>Processing...</div>} | ||||||
|  |     </> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| import { HeartIcon } from '@heroicons/react/outline' |  | ||||||
| import { Button } from 'web/components/button' |  | ||||||
| import { formatMoney } from 'common/util/format' |  | ||||||
| import clsx from 'clsx' | import clsx from 'clsx' | ||||||
|  | import { HeartIcon } from '@heroicons/react/outline' | ||||||
|  | 
 | ||||||
|  | import { Button } from 'web/components/button' | ||||||
|  | import { formatMoney, shortFormatNumber } from 'common/util/format' | ||||||
| import { Col } from 'web/components/layout/col' | import { Col } from 'web/components/layout/col' | ||||||
| import { Tooltip } from '../tooltip' | import { Tooltip } from '../tooltip' | ||||||
| 
 | 
 | ||||||
|  | @ -51,7 +52,7 @@ export function TipButton(props: { | ||||||
|                   : 'sm:text-2xs text-[0.5rem]' |                   : 'sm:text-2xs text-[0.5rem]' | ||||||
|               )} |               )} | ||||||
|             > |             > | ||||||
|               {totalTipped} |               {shortFormatNumber(totalTipped)} | ||||||
|             </div> |             </div> | ||||||
|           )} |           )} | ||||||
|         </Col> |         </Col> | ||||||
|  |  | ||||||
|  | @ -21,11 +21,6 @@ export const useLiquidity = (contractId: string) => { | ||||||
| export const useUserLiquidity = (contract: CPMMContract, userId: string) => { | export const useUserLiquidity = (contract: CPMMContract, userId: string) => { | ||||||
|   const liquidities = useLiquidity(contract.id) |   const liquidities = useLiquidity(contract.id) | ||||||
| 
 | 
 | ||||||
|   const userShares = getUserLiquidityShares( |   const userShares = getUserLiquidityShares(userId, contract, liquidities ?? []) | ||||||
|     userId, |  | ||||||
|     contract, |  | ||||||
|     liquidities ?? [], |  | ||||||
|     true |  | ||||||
|   ) |  | ||||||
|   return userShares |   return userShares | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -42,8 +42,8 @@ export function changeUserInfo(params: any) { | ||||||
|   return call(getFunctionUrl('changeuserinfo'), 'POST', params) |   return call(getFunctionUrl('changeuserinfo'), 'POST', params) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function addLiquidity(params: any) { | export function addSubsidy(params: any) { | ||||||
|   return call(getFunctionUrl('addliquidity'), 'POST', params) |   return call(getFunctionUrl('addsubsidy'), 'POST', params) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function addCommentBounty(params: any) { | export function addCommentBounty(params: any) { | ||||||
|  | @ -54,10 +54,6 @@ export function awardCommentBounty(params: any) { | ||||||
|   return call(getFunctionUrl('awardcommentbounty'), 'POST', params) |   return call(getFunctionUrl('awardcommentbounty'), 'POST', params) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function withdrawLiquidity(params: any) { |  | ||||||
|   return call(getFunctionUrl('withdrawliquidity'), 'POST', params) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function createMarket(params: any) { | export function createMarket(params: any) { | ||||||
|   return call(getFunctionUrl('createmarket'), 'POST', params) |   return call(getFunctionUrl('createmarket'), 'POST', params) | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user