Make liquidity great again (#1020)

* add subsidy

* drizzle liquidity

* update liquidity panel

* remove addliquidity

* update cloud functions index

* remove json endpoints

* imports

* drizzle liquidity: add velocity; dev script; run every minute

* adjust speed

* logging

* liquidity button, dialog

* modal size

* modal

* info table

* pay back excess liquidity

* remove client withdrawal

* house liquidity subsidy

* disable liquidity button if market resolved or closed

* format tip amount
This commit is contained in:
mantikoros 2022-10-10 21:56:16 -05:00 committed by GitHub
parent 8bb9885aee
commit 0ec15ff2f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 421 additions and 519 deletions

View File

@ -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 }
} }

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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(' ')

View File

@ -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)
})
}

View 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()
})
}

View 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)
})
}

View File

@ -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,

View File

@ -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 }
}) })

View File

@ -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)

View File

@ -0,0 +1,8 @@
import { initAdmin } from './script-init'
initAdmin()
import { drizzleLiquidity } from '../drizzle-liquidity'
if (require.main === module) {
drizzleLiquidity().then(() => process.exit())
}

View File

@ -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)

View File

@ -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()

View File

@ -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>
</> </>

View File

@ -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

View File

@ -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>
)
}

View 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>
)
}

View 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>}
</>
)
}

View File

@ -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>

View File

@ -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
} }

View File

@ -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)
} }